google-gax 0.1.1
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 +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
|