google-gax 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Rakefile +15 -0
- data/lib/google/gax.rb +283 -0
- data/lib/google/gax/api_callable.rb +356 -0
- data/lib/google/gax/errors.rb +51 -0
- data/lib/google/gax/grpc.rb +94 -0
- data/lib/google/gax/path_template.rb +248 -0
- data/lib/google/gax/settings.rb +213 -0
- data/lib/google/gax/version.rb +34 -0
- data/spec/google/gax/api_callable_spec.rb +232 -0
- data/spec/google/gax/path_template_spec.rb +159 -0
- data/spec/google/gax/settings_spec.rb +134 -0
- data/spec/spec_helper.rb +8 -0
- metadata +182 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: d5a83ab24363b35811939797cf9bf42efb7f6c9d
|
4
|
+
data.tar.gz: c57b1a19fbce28c086d469989edba2ecd269da1c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 9ab3cfa6e5f2addb6b41f640d29a660fae38b66d261835e04fde29736370e0bf9a87a017d8b4eb8d4545e13d53419bf391f598e5c8128a00de58eef129a8c2b1
|
7
|
+
data.tar.gz: a013ae626c7286b2ded6e1a315e88be2a1e107aabdfb17554c315edb487b051df8274dc4c92cf256db5566338ae4add38491dc3935d7995b03b88888164d1600
|
data/Rakefile
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
2
|
+
|
3
|
+
Bundler.setup :default, :development
|
4
|
+
|
5
|
+
require 'rspec/core'
|
6
|
+
require 'rspec/core/rake_task'
|
7
|
+
|
8
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
9
|
+
spec.pattern = FileList['spec/**/*_spec.rb']
|
10
|
+
end
|
11
|
+
|
12
|
+
require 'rubocop/rake_task'
|
13
|
+
RuboCop::RakeTask.new(:rubocop)
|
14
|
+
|
15
|
+
task default: [:spec, :rubocop]
|
data/lib/google/gax.rb
ADDED
@@ -0,0 +1,283 @@
|
|
1
|
+
# Copyright 2016, Google Inc.
|
2
|
+
# All rights reserved.
|
3
|
+
#
|
4
|
+
# Redistribution and use in source and binary forms, with or without
|
5
|
+
# modification, are permitted provided that the following conditions are
|
6
|
+
# met:
|
7
|
+
#
|
8
|
+
# * Redistributions of source code must retain the above copyright
|
9
|
+
# notice, this list of conditions and the following disclaimer.
|
10
|
+
# * Redistributions in binary form must reproduce the above
|
11
|
+
# copyright notice, this list of conditions and the following disclaimer
|
12
|
+
# in the documentation and/or other materials provided with the
|
13
|
+
# distribution.
|
14
|
+
# * Neither the name of Google Inc. nor the names of its
|
15
|
+
# contributors may be used to endorse or promote products derived from
|
16
|
+
# this software without specific prior written permission.
|
17
|
+
#
|
18
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
19
|
+
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
20
|
+
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
21
|
+
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
22
|
+
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
23
|
+
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
24
|
+
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
25
|
+
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
26
|
+
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
27
|
+
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
28
|
+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
29
|
+
|
30
|
+
require 'google/gax/api_callable'
|
31
|
+
require 'google/gax/errors'
|
32
|
+
require 'google/gax/grpc'
|
33
|
+
require 'google/gax/path_template'
|
34
|
+
require 'google/gax/settings'
|
35
|
+
require 'google/gax/version'
|
36
|
+
|
37
|
+
module Google
|
38
|
+
module Gax
|
39
|
+
# Encapsulates the call settings for an ApiCallable
|
40
|
+
# @!attribute [r] timeout
|
41
|
+
# @return [Numeric]
|
42
|
+
# @!attribute [r] retry_options
|
43
|
+
# @return [RetryOptions]
|
44
|
+
# @!attribute [r] page_descriptor
|
45
|
+
# @return [PageDescriptor]
|
46
|
+
# @!attribute [r] bundle_descriptor
|
47
|
+
# @return [BundleDescriptor]
|
48
|
+
class CallSettings
|
49
|
+
attr_reader :timeout, :retry_options, :page_descriptor, :bundler,
|
50
|
+
:bundle_descriptor
|
51
|
+
|
52
|
+
# @param timeout [Numeric] The client-side timeout for API calls. This
|
53
|
+
# parameter is ignored for retrying calls.
|
54
|
+
# @param retry_options [RetryOptions] The configuration for retrying upon
|
55
|
+
# transient error. If set to None, this call will not retry.
|
56
|
+
# @param page_descriptor [PageDescriptor] indicates the structure of page
|
57
|
+
# streaming to be performed. If set to None, page streaming is not
|
58
|
+
# performed.
|
59
|
+
# @param bundler orchestrates bundling. If None, bundling is not
|
60
|
+
# performed.
|
61
|
+
# @param bundle_descriptor [BundleDescriptor] indicates the structure of
|
62
|
+
# the bundle. If None, bundling is not performed.
|
63
|
+
def initialize(
|
64
|
+
timeout: 30, retry_options: nil, page_descriptor: nil,
|
65
|
+
bundler: nil, bundle_descriptor: nil)
|
66
|
+
@timeout = timeout
|
67
|
+
@retry_options = retry_options
|
68
|
+
@page_descriptor = page_descriptor
|
69
|
+
@bundler = bundler
|
70
|
+
@bundle_descriptor = bundle_descriptor
|
71
|
+
end
|
72
|
+
|
73
|
+
# @return true when it has retry codes.
|
74
|
+
def retry_codes?
|
75
|
+
@retry_options && @retry_options.retry_codes
|
76
|
+
end
|
77
|
+
|
78
|
+
# @return true when it has valid bundler configuration.
|
79
|
+
def bundler?
|
80
|
+
@bundler && @bundle_descriptor
|
81
|
+
end
|
82
|
+
|
83
|
+
# Creates a new CallSetting instance which is based on this but merged
|
84
|
+
# settings from options.
|
85
|
+
# @param options [CallOptions, nil] The overriding call settings.
|
86
|
+
# @return a new merged call settings.
|
87
|
+
def merge(options)
|
88
|
+
unless options
|
89
|
+
return CallSettings.new(
|
90
|
+
timeout: @timeout,
|
91
|
+
retry_options: @retry_options,
|
92
|
+
page_descriptor: @page_descriptor,
|
93
|
+
bundler: @bundler,
|
94
|
+
bundle_descriptor: @bundle_descriptor)
|
95
|
+
end
|
96
|
+
|
97
|
+
timeout = if options.timeout == :OPTION_INHERIT
|
98
|
+
@timeout
|
99
|
+
else
|
100
|
+
options.timeout
|
101
|
+
end
|
102
|
+
retry_options = if options.retry_options == :OPTION_INHERIT
|
103
|
+
@retry_options
|
104
|
+
else
|
105
|
+
options.retry_options
|
106
|
+
end
|
107
|
+
page_descriptor = @page_descriptor if options.is_page_streaming
|
108
|
+
|
109
|
+
CallSettings.new(
|
110
|
+
timeout: timeout,
|
111
|
+
retry_options: retry_options,
|
112
|
+
page_descriptor: page_descriptor,
|
113
|
+
bundler: @bundler,
|
114
|
+
bundle_descriptor: @bundle_descriptor)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# Encapsulates the overridable settings for a particular API call
|
119
|
+
# @!attribute [r] timeout
|
120
|
+
# @return [Numeric, :OPTION_INHERIT]
|
121
|
+
# @!attribute [r] retry_options
|
122
|
+
# @return [RetryOptions, :OPTION_INHERIT]
|
123
|
+
# @!attribute [r] is_page_streaming
|
124
|
+
# @return [true, false, :OPTION_INHERIT]
|
125
|
+
class CallOptions
|
126
|
+
attr_reader :timeout, :retry_options, :is_page_streaming
|
127
|
+
|
128
|
+
# @param timeout [Numeric, :OPTION_INHERIT]
|
129
|
+
# The client-side timeout for API calls.
|
130
|
+
# @param retry_options [RetryOptions, :OPTION_INHERIT]
|
131
|
+
# The configuration for retrying upon transient error.
|
132
|
+
# If set to nil, this call will not retry.
|
133
|
+
# @param is_page_streaming [true, false, :OPTION_INHERIT]
|
134
|
+
# If set and the call is configured for page streaming, page streaming
|
135
|
+
# is performed.
|
136
|
+
def initialize(
|
137
|
+
timeout: :OPTION_INHERIT,
|
138
|
+
retry_options: :OPTION_INHERIT,
|
139
|
+
is_page_streaming: :OPTION_INHERIT)
|
140
|
+
@timeout = timeout
|
141
|
+
@retry_options = retry_options
|
142
|
+
@is_page_streaming = is_page_streaming
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
# Describes the structure of a page-streaming call.
|
147
|
+
class PageDescriptor < Struct.new(
|
148
|
+
:request_page_token_field,
|
149
|
+
:response_page_token_field,
|
150
|
+
:resource_field)
|
151
|
+
end
|
152
|
+
|
153
|
+
# Per-call configurable settings for retrying upon transient failure.
|
154
|
+
class RetryOptions < Struct.new(:retry_codes, :backoff_settings)
|
155
|
+
# @!attribute retry_codes
|
156
|
+
# @return [Array<Grpc::Code>] a list of exceptions upon which
|
157
|
+
# a retry should be attempted.
|
158
|
+
# @!attribute backoff_settings
|
159
|
+
# @return [BackoffSettings] configuring the retry exponential
|
160
|
+
# backoff algorithm.
|
161
|
+
end
|
162
|
+
|
163
|
+
# Parameters to the exponential backoff algorithm for retrying.
|
164
|
+
class BackoffSettings < Struct.new(
|
165
|
+
:initial_retry_delay_millis,
|
166
|
+
:retry_delay_multiplier,
|
167
|
+
:max_retry_delay_millis,
|
168
|
+
:initial_rpc_timeout_millis,
|
169
|
+
:rpc_timeout_multiplier,
|
170
|
+
:max_rpc_timeout_millis,
|
171
|
+
:total_timeout_millis)
|
172
|
+
# @!attribute initial_retry_delay_millis
|
173
|
+
# @return [Numeric] the initial delay time, in milliseconds,
|
174
|
+
# between the completion of the first failed request and the
|
175
|
+
# initiation of the first retrying request.
|
176
|
+
# @!attribute retry_delay_multiplier
|
177
|
+
# @return [Numeric] the multiplier by which to increase the
|
178
|
+
# delay time between the completion of failed requests, and
|
179
|
+
# the initiation of the subsequent retrying request.
|
180
|
+
# @!attribute max_retry_delay_millis
|
181
|
+
# @return [Numeric] the maximum delay time, in milliseconds,
|
182
|
+
# between requests. When this value is reached,
|
183
|
+
# +retry_delay_multiplier+ will no longer be used to
|
184
|
+
# increase delay time.
|
185
|
+
# @!attribute initial_rpc_timeout_millis
|
186
|
+
# @return [Numeric] the initial timeout parameter to the request.
|
187
|
+
# @!attribute rpc_timeout_multiplier
|
188
|
+
# @return [Numeric] the multiplier by which to increase the
|
189
|
+
# timeout parameter between failed requests.
|
190
|
+
# @!attribute max_rpc_timeout_millis
|
191
|
+
# @return [Numeric] the maximum timeout parameter, in
|
192
|
+
# milliseconds, for a request. When this value is reached,
|
193
|
+
# +rpc_timeout_multiplier+ will no longer be used to
|
194
|
+
# increase the timeout.
|
195
|
+
# @!attribute total_timeout_millis
|
196
|
+
# @return [Numeric] the total time, in milliseconds, starting
|
197
|
+
# from when the initial request is sent, after which an
|
198
|
+
# error will be returned, regardless of the retrying
|
199
|
+
# attempts made meanwhile.
|
200
|
+
end
|
201
|
+
|
202
|
+
# Describes the structure of bundled call.
|
203
|
+
#
|
204
|
+
# request_discriminator_fields may include '.' as a separator, which is
|
205
|
+
# used to indicate object traversal. This allows fields in nested objects
|
206
|
+
# to be used to determine what requests to bundle.
|
207
|
+
class BundleDescriptor < Struct.new(
|
208
|
+
:bundled_field,
|
209
|
+
:request_discriminator_fields,
|
210
|
+
:subresponse_field)
|
211
|
+
# @!attribute bundled_field
|
212
|
+
# @return [String] the repeated field in the request message
|
213
|
+
# that will have its elements aggregated by bundling
|
214
|
+
# @!attribute request_discriminator_fields
|
215
|
+
# @return [Array<String>] a list of fields in the target
|
216
|
+
# request message class that are used to determine which
|
217
|
+
# messages should be bundled together.
|
218
|
+
# @!attribute subresponse_field
|
219
|
+
# @return [String] an optional field, when present it
|
220
|
+
# indicates the field in the response message that should be
|
221
|
+
# used to demultiplex the response into multiple response
|
222
|
+
# messages.
|
223
|
+
def initialize(bundled_field, request_discriminator_fields,
|
224
|
+
subresponse_field: nil)
|
225
|
+
super(bundled_field, request_discriminator_fields, subresponse_field)
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
# Holds values used to configure bundling.
|
230
|
+
#
|
231
|
+
# The xxx_threshold attributes are used to configure when the bundled
|
232
|
+
# request should be made.
|
233
|
+
class BundleOptions < Struct.new(
|
234
|
+
:element_count_threshold,
|
235
|
+
:element_count_limit,
|
236
|
+
:request_byte_threshold,
|
237
|
+
:request_byte_limit,
|
238
|
+
:delay_threshold)
|
239
|
+
# @!attribute element_count_threshold
|
240
|
+
# @return [Numeric] the bundled request will be sent once the
|
241
|
+
# count of outstanding elements in the repeated field
|
242
|
+
# reaches this value.
|
243
|
+
# @!attribute element_count_limit
|
244
|
+
# @return [Numeric] represents a hard limit on the number of
|
245
|
+
# elements in the repeated field of the bundle; if adding a
|
246
|
+
# request to a bundle would exceed this value, the bundle is
|
247
|
+
# sent and the new request is added to a fresh bundle. It is
|
248
|
+
# invalid for a single request to exceed this limit.
|
249
|
+
# @!attribute request_byte_threshold
|
250
|
+
# @return [Numeric] the bundled request will be sent once the
|
251
|
+
# count of bytes in the request reaches this value. Note
|
252
|
+
# that this value is pessimistically approximated by summing
|
253
|
+
# the bytesizes of the elements in the repeated field, and
|
254
|
+
# therefore may be an under-approximation.
|
255
|
+
# @!attribute request_byte_limit
|
256
|
+
# @return [Numeric] represents a hard limit on the size of the
|
257
|
+
# bundled request; if adding a request to a bundle would
|
258
|
+
# exceed this value, the bundle is sent and the new request
|
259
|
+
# is added to a fresh bundle. It is invalid for a single
|
260
|
+
# request to exceed this limit. Note that this value is
|
261
|
+
# pessimistically approximated by summing the bytesizes of
|
262
|
+
# the elements in the repeated field, with a buffer applied
|
263
|
+
# to correspond to the resulting under-approximation.
|
264
|
+
# @!attribute delay_threshold
|
265
|
+
# @return [Numeric] the bundled request will be sent this
|
266
|
+
# amount of time after the first element in the bundle was
|
267
|
+
# added to it.
|
268
|
+
def initialize(
|
269
|
+
element_count_threshold: 0,
|
270
|
+
element_count_limit: 0,
|
271
|
+
request_byte_threshold: 0,
|
272
|
+
request_byte_limit: 0,
|
273
|
+
delay_threshold: 0)
|
274
|
+
super(
|
275
|
+
element_count_threshold,
|
276
|
+
element_count_limit,
|
277
|
+
request_byte_threshold,
|
278
|
+
request_byte_limit,
|
279
|
+
delay_threshold)
|
280
|
+
end
|
281
|
+
end
|
282
|
+
end
|
283
|
+
end
|
@@ -0,0 +1,356 @@
|
|
1
|
+
# Copyright 2016, Google Inc.
|
2
|
+
# All rights reserved.
|
3
|
+
#
|
4
|
+
# Redistribution and use in source and binary forms, with or without
|
5
|
+
# modification, are permitted provided that the following conditions are
|
6
|
+
# met:
|
7
|
+
#
|
8
|
+
# * Redistributions of source code must retain the above copyright
|
9
|
+
# notice, this list of conditions and the following disclaimer.
|
10
|
+
# * Redistributions in binary form must reproduce the above
|
11
|
+
# copyright notice, this list of conditions and the following disclaimer
|
12
|
+
# in the documentation and/or other materials provided with the
|
13
|
+
# distribution.
|
14
|
+
# * Neither the name of Google Inc. nor the names of its
|
15
|
+
# contributors may be used to endorse or promote products derived from
|
16
|
+
# this software without specific prior written permission.
|
17
|
+
#
|
18
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
19
|
+
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
20
|
+
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
21
|
+
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
22
|
+
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
23
|
+
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
24
|
+
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
25
|
+
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
26
|
+
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
27
|
+
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
28
|
+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
29
|
+
|
30
|
+
require 'time'
|
31
|
+
|
32
|
+
require 'google/gax/errors'
|
33
|
+
require 'google/gax/grpc'
|
34
|
+
|
35
|
+
# rubocop:disable Metrics/ModuleLength
|
36
|
+
module Google
|
37
|
+
module Gax
|
38
|
+
MILLIS_PER_SECOND = 1000.0
|
39
|
+
|
40
|
+
# A class to provide the Enumerable interface for page-streaming method.
|
41
|
+
# PagedEnumerable assumes that the API call returns a message for a page
|
42
|
+
# which holds a list of resources and the token to the next page.
|
43
|
+
#
|
44
|
+
# PagedEnumerable provides the enumerations over the resource data,
|
45
|
+
# and also provides the enumerations over the pages themselves.
|
46
|
+
#
|
47
|
+
# @attribute [r] page
|
48
|
+
# @return [Page] The current page object.
|
49
|
+
class PagedEnumerable
|
50
|
+
# A class to represent a page in a PagedEnumerable. This also implements
|
51
|
+
# Enumerable, so it can iterate over the resource elements.
|
52
|
+
#
|
53
|
+
# @attribute [r] response
|
54
|
+
# @return [Object] The actual response object.
|
55
|
+
class Page
|
56
|
+
include Enumerable
|
57
|
+
attr_reader :response
|
58
|
+
|
59
|
+
# @param response [Object]
|
60
|
+
# The response object for the page.
|
61
|
+
# @param response_page_token_field [String]
|
62
|
+
# The name of the field in response which holds the next page token.
|
63
|
+
# @param resource_field [String]
|
64
|
+
# The name of the field in response which holds the resources.
|
65
|
+
def initialize(response, response_page_token_field, resource_field)
|
66
|
+
@response = response
|
67
|
+
@response_page_token_field = response_page_token_field
|
68
|
+
@resource_field = resource_field
|
69
|
+
end
|
70
|
+
|
71
|
+
# Creates another instance of Page with replacing the new response.
|
72
|
+
# @param response [Object] a new response object.
|
73
|
+
def dup_with(response)
|
74
|
+
self.class.new(response, @response_page_token_field, @resource_field)
|
75
|
+
end
|
76
|
+
|
77
|
+
# Iterate over the resources.
|
78
|
+
# @yield [Object] Gives the resource objects in the page.
|
79
|
+
def each
|
80
|
+
@response[@resource_field].each do |obj|
|
81
|
+
yield obj
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def next_page_token
|
86
|
+
@response[@response_page_token_field]
|
87
|
+
end
|
88
|
+
|
89
|
+
# Truthiness of next_page_token.
|
90
|
+
def next_page_token?
|
91
|
+
!@response.nil? && !next_page_token.nil? && next_page_token != 0 &&
|
92
|
+
(!next_page_token.respond_to?(:empty) || !next_page_token.empty?)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
include Enumerable
|
97
|
+
attr_reader :page
|
98
|
+
|
99
|
+
# @param a_func [Proc]
|
100
|
+
# A proc to update the response object.
|
101
|
+
# @param request_page_token_field [String]
|
102
|
+
# The name of the field in request which will have the page token.
|
103
|
+
# @param response_page_token_field [String]
|
104
|
+
# The name of the field in the response which holds the next page token.
|
105
|
+
# @param resource_field [String]
|
106
|
+
# The name of the field in the response which holds the resources.
|
107
|
+
def initialize(a_func, request_page_token_field,
|
108
|
+
response_page_token_field, resource_field)
|
109
|
+
@func = a_func
|
110
|
+
@request_page_token_field = request_page_token_field
|
111
|
+
@page = Page.new(nil, response_page_token_field, resource_field)
|
112
|
+
end
|
113
|
+
|
114
|
+
# Initiate the streaming with the requests and keywords.
|
115
|
+
# @param request [Object]
|
116
|
+
# The initial request object.
|
117
|
+
# @param kwargs [Hash]
|
118
|
+
# Other keyword arguments to be passed to a_func.
|
119
|
+
# @return [PagedEnumerable]
|
120
|
+
# returning self for further uses.
|
121
|
+
def start(request, **kwargs)
|
122
|
+
@request = request
|
123
|
+
@kwargs = kwargs
|
124
|
+
@page = @page.dup_with(@func.call(@request, **@kwargs))
|
125
|
+
self
|
126
|
+
end
|
127
|
+
|
128
|
+
# True if it's already started.
|
129
|
+
def started?
|
130
|
+
!@request.nil?
|
131
|
+
end
|
132
|
+
|
133
|
+
# Iterate over the resources.
|
134
|
+
# @yield [Object] Gives the resource objects in the stream.
|
135
|
+
# @raise [RuntimeError] if it's not started yet.
|
136
|
+
def each
|
137
|
+
each_page do |page|
|
138
|
+
page.each do |obj|
|
139
|
+
yield obj
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
# Iterate over the pages.
|
145
|
+
# @yield [Page] Gives the pages in the stream.
|
146
|
+
# @raise [RuntimeError] if it's not started yet.
|
147
|
+
def each_page
|
148
|
+
raise 'not started!' unless started?
|
149
|
+
yield @page
|
150
|
+
loop do
|
151
|
+
break unless next_page?
|
152
|
+
yield next_page
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
# True if it has the next page.
|
157
|
+
def next_page?
|
158
|
+
@page.next_page_token?
|
159
|
+
end
|
160
|
+
|
161
|
+
# Update the response in the current page.
|
162
|
+
# @return [Page] the new page object.
|
163
|
+
def next_page
|
164
|
+
return unless next_page?
|
165
|
+
@request[@request_page_token_field] = @page.next_page_token
|
166
|
+
@page = @page.dup_with(@func.call(@request, **@kwargs))
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
# rubocop:disable Metrics/AbcSize
|
171
|
+
|
172
|
+
# Converts an rpc call into an API call governed by the settings.
|
173
|
+
#
|
174
|
+
# In typical usage, +func+ will be a proc used to make an rpc request.
|
175
|
+
# This will mostly likely be a bound method from a request stub used to make
|
176
|
+
# an rpc call.
|
177
|
+
#
|
178
|
+
# The result is created by applying a series of function decorators
|
179
|
+
# defined in this module to +func+. +settings+ is used to determine
|
180
|
+
# which function decorators to apply.
|
181
|
+
#
|
182
|
+
# The result is another proc which for most values of +settings+ has the
|
183
|
+
# same signature as the original. Only when +settings+ configures bundling
|
184
|
+
# does the signature change.
|
185
|
+
#
|
186
|
+
# @param func [Proc] used to make a bare rpc call
|
187
|
+
# @param settings [CallSettings provides the settings for this call
|
188
|
+
# @return [Proc] a bound method on a request stub used to make an rpc call
|
189
|
+
# @raise [StandardError] if +settings+ has incompatible values,
|
190
|
+
# e.g, if bundling and page_streaming are both configured
|
191
|
+
def create_api_call(func, settings)
|
192
|
+
api_call = if settings.retry_codes?
|
193
|
+
_retryable(func, settings.retry_options)
|
194
|
+
else
|
195
|
+
_add_timeout_arg(func, settings.timeout)
|
196
|
+
end
|
197
|
+
|
198
|
+
if settings.page_descriptor
|
199
|
+
if settings.bundler?
|
200
|
+
raise 'ApiCallable has incompatible settings: ' \
|
201
|
+
'bundling and page streaming'
|
202
|
+
end
|
203
|
+
return _page_streamable(
|
204
|
+
api_call,
|
205
|
+
settings.page_descriptor.request_page_token_field,
|
206
|
+
settings.page_descriptor.response_page_token_field,
|
207
|
+
settings.page_descriptor.resource_field)
|
208
|
+
end
|
209
|
+
if settings.bundler?
|
210
|
+
return _bundleable(api_call, settings.bundle_descriptor,
|
211
|
+
settings.bundler)
|
212
|
+
end
|
213
|
+
|
214
|
+
_catch_errors(api_call)
|
215
|
+
end
|
216
|
+
|
217
|
+
# Updates a_func to wrap exceptions with GaxError
|
218
|
+
#
|
219
|
+
# @param a_func [Proc]
|
220
|
+
# @param errors [Array<Exception>] Configures the exceptions to wrap.
|
221
|
+
# @return [Proc] A proc that will wrap certain exceptions with GaxError
|
222
|
+
def _catch_errors(a_func, errors: Grpc::API_ERRORS)
|
223
|
+
proc do |request, **kwargs|
|
224
|
+
begin
|
225
|
+
a_func.call(request, **kwargs)
|
226
|
+
rescue => err
|
227
|
+
if errors.any? { |eclass| err.is_a? eclass }
|
228
|
+
raise GaxError.new('RPC failed', cause: err)
|
229
|
+
else
|
230
|
+
raise err
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
# Creates a proc that transforms an API call into a bundling call.
|
237
|
+
#
|
238
|
+
# It transform a_func from an API call that receives the requests and
|
239
|
+
# returns the response into a proc that receives the same request, and
|
240
|
+
# returns a +Google::Gax::Bundling::Event+.
|
241
|
+
#
|
242
|
+
# The returned Event object can be used to obtain the eventual result of the
|
243
|
+
# bundled call.
|
244
|
+
#
|
245
|
+
# @param a_func [Proc] an API call that supports bundling.
|
246
|
+
# @param desc [BundleDescriptor] describes the bundling that
|
247
|
+
# +a_func+ supports.
|
248
|
+
# @param bundler orchestrates bundling.
|
249
|
+
# @return [Proc] A proc takes the API call's request and returns
|
250
|
+
# an Event object.
|
251
|
+
def _bundleable(a_func, desc, bundler)
|
252
|
+
proc do |request|
|
253
|
+
the_id = bundling.compute_bundle_id(
|
254
|
+
request,
|
255
|
+
desc.request_discriminator_fields)
|
256
|
+
return bundler.schedule(a_func, the_id, desc, request)
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
# Creates a proc that yields an iterable to performs page-streaming.
|
261
|
+
#
|
262
|
+
# @param a_func [Proc] an API call that is page streaming.
|
263
|
+
# @param request_page_token_field [String] The field of the page
|
264
|
+
# token in the request.
|
265
|
+
# @param response_page_token_field [String] The field of the next
|
266
|
+
# page token in the response.
|
267
|
+
# @param resource_field [String] The field to be streamed.
|
268
|
+
# @return [Proc] A proc that returns an iterable over the specified field.
|
269
|
+
def _page_streamable(
|
270
|
+
a_func,
|
271
|
+
request_page_token_field,
|
272
|
+
response_page_token_field,
|
273
|
+
resource_field)
|
274
|
+
enumerable = PagedEnumerable.new(a_func,
|
275
|
+
request_page_token_field,
|
276
|
+
response_page_token_field,
|
277
|
+
resource_field)
|
278
|
+
enumerable.method(:start)
|
279
|
+
end
|
280
|
+
|
281
|
+
# rubocop:disable Metrics/MethodLength
|
282
|
+
|
283
|
+
# Creates a proc equivalent to a_func, but that retries on certain
|
284
|
+
# exceptions.
|
285
|
+
#
|
286
|
+
# @param a_func [Proc]
|
287
|
+
# @param retry_options [RetryOptions] Configures the exceptions
|
288
|
+
# upon which the proc should retry, and the parameters to the
|
289
|
+
# exponential backoff retry algorithm.
|
290
|
+
# @return [Proc] A proc that will retry on exception.
|
291
|
+
def _retryable(a_func, retry_options)
|
292
|
+
delay_mult = retry_options.backoff_settings.retry_delay_multiplier
|
293
|
+
max_delay = (retry_options.backoff_settings.max_retry_delay_millis /
|
294
|
+
MILLIS_PER_SECOND)
|
295
|
+
timeout_mult = retry_options.backoff_settings.rpc_timeout_multiplier
|
296
|
+
max_timeout = (retry_options.backoff_settings.max_rpc_timeout_millis /
|
297
|
+
MILLIS_PER_SECOND)
|
298
|
+
total_timeout = (retry_options.backoff_settings.total_timeout_millis /
|
299
|
+
MILLIS_PER_SECOND)
|
300
|
+
|
301
|
+
proc do |request, **kwargs|
|
302
|
+
delay = retry_options.backoff_settings.initial_retry_delay_millis
|
303
|
+
timeout = (retry_options.backoff_settings.initial_rpc_timeout_millis /
|
304
|
+
MILLIS_PER_SECOND)
|
305
|
+
exc = nil
|
306
|
+
result = nil
|
307
|
+
now = Time.now
|
308
|
+
deadline = now + total_timeout
|
309
|
+
|
310
|
+
while now < deadline
|
311
|
+
begin
|
312
|
+
exc = nil
|
313
|
+
result = _add_timeout_arg(a_func, timeout).call(request, **kwargs)
|
314
|
+
break
|
315
|
+
rescue => exception
|
316
|
+
unless exception.respond_to?(:code) &&
|
317
|
+
retry_options.retry_codes.include?(exception.code)
|
318
|
+
raise RetryError.new('Exception occurred in retry method that ' \
|
319
|
+
'was not classified as transient',
|
320
|
+
cause: exception)
|
321
|
+
end
|
322
|
+
exc = RetryError.new('Retry total timeout exceeded with exception',
|
323
|
+
cause: exception)
|
324
|
+
sleep(rand(delay) / MILLIS_PER_SECOND)
|
325
|
+
now = Time.now
|
326
|
+
delay = [delay * delay_mult, max_delay].min
|
327
|
+
timeout = [timeout * timeout_mult, max_timeout, deadline - now].min
|
328
|
+
end
|
329
|
+
end
|
330
|
+
raise exc unless exc.nil?
|
331
|
+
result
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
# Updates +a_func+ so that it gets called with the timeout as its final arg.
|
336
|
+
#
|
337
|
+
# This converts a proc, a_func, into another proc with an additional
|
338
|
+
# positional arg.
|
339
|
+
#
|
340
|
+
# @param a_func [Proc] a proc to be updated
|
341
|
+
# @param timeout [Numeric] to be added to the original proc as it
|
342
|
+
# final positional arg.
|
343
|
+
# @return [Proc] the original proc updated to the timeout arg
|
344
|
+
def _add_timeout_arg(a_func, timeout)
|
345
|
+
proc do |request, **kwargs|
|
346
|
+
kwargs[:timeout] = timeout
|
347
|
+
a_func.call(request, **kwargs)
|
348
|
+
end
|
349
|
+
end
|
350
|
+
|
351
|
+
module_function :create_api_call, :_catch_errors, :_bundleable,
|
352
|
+
:_page_streamable, :_retryable, :_add_timeout_arg
|
353
|
+
private_class_method :_catch_errors, :_bundleable, :_page_streamable,
|
354
|
+
:_retryable, :_add_timeout_arg
|
355
|
+
end
|
356
|
+
end
|