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.
@@ -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
@@ -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]
@@ -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