gapic-common 0.0.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,40 @@
1
+ # Copyright 2019 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 "grpc"
16
+ require "grpc/errors"
17
+ require "grpc/google_rpc_status_utils"
18
+ require "google/protobuf/well_known_types"
19
+ # Required in order to deserialize common error detail proto types
20
+ require "google/rpc/error_details_pb"
21
+
22
+ module GRPC
23
+ class BadStatus < StandardError
24
+ def status_details
25
+ rpc_status = to_rpc_status
26
+
27
+ return nil.to_a if rpc_status.nil?
28
+
29
+ rpc_status.details.map do |detail|
30
+ begin
31
+ detail_type = Google::Protobuf::DescriptorPool.generated_pool.lookup detail.type_name
32
+ detail = detail.unpack detail_type.msgclass if detail_type
33
+ detail
34
+ rescue Google::Protobuf::ParseError
35
+ detail
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,43 @@
1
+ # Copyright 2019 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/operation/retry_policy"
16
+ require "google/protobuf/well_known_types"
17
+ require "gapic/common/version"
18
+
19
+ module Gapic
20
+ # A collection of common header values.
21
+ module Headers
22
+ ##
23
+ # @param ruby_version [String] The ruby version. Defaults to `RUBY_VERSION`.
24
+ # @param lib_name [String] The client library name.
25
+ # @param lib_version [String] The client library version.
26
+ # @param gax_version [String] The Gapic version. Defaults to `Gapic::Common::VERSION`.
27
+ # @param gapic_version [String] The Gapic version.
28
+ # @param grpc_version [String] The GRPC version. Defaults to `GRPC::VERSION`.
29
+ def self.x_goog_api_client ruby_version: nil, lib_name: nil, lib_version: nil,
30
+ gax_version: nil, gapic_version: nil, grpc_version: nil
31
+ ruby_version ||= ::RUBY_VERSION
32
+ gax_version ||= ::Gapic::Common::VERSION
33
+ grpc_version ||= ::GRPC::VERSION if defined? ::GRPC
34
+
35
+ x_goog_api_client_header = ["gl-ruby/#{ruby_version}"]
36
+ x_goog_api_client_header << "#{lib_name}/#{lib_version}" if lib_name
37
+ x_goog_api_client_header << "gax/#{gax_version}"
38
+ x_goog_api_client_header << "gapic/#{gapic_version}" if gapic_version
39
+ x_goog_api_client_header << "grpc/#{grpc_version}" if grpc_version
40
+ x_goog_api_client_header.join " ".freeze
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,278 @@
1
+ # Copyright 2019 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/operation/retry_policy"
16
+ require "google/protobuf/well_known_types"
17
+
18
+ module Gapic
19
+ # A class used to wrap Google::Longrunning::Operation objects. This class provides helper methods to check the
20
+ # status of an Operation
21
+ #
22
+ # @example Checking Operation status
23
+ # # this example assumes both api_client and operations_client
24
+ # # already exist.
25
+ # require "gapic/operation"
26
+ #
27
+ # op = Gapic::Operation.new(
28
+ # api_client.method_that_returns_longrunning_operation(),
29
+ # operations_client,
30
+ # Google::Example::ResultType,
31
+ # Google::Example::MetadataType
32
+ # )
33
+ #
34
+ # op.done? # => false
35
+ # op.reload! # => operation completed
36
+ #
37
+ # if op.done?
38
+ # results = op.results
39
+ # handle_error(results) if op.error?
40
+ # # Handle results.
41
+ # end
42
+ #
43
+ # @example Working with callbacks
44
+ # # this example assumes both api_client and operations_client
45
+ # # already exist.
46
+ # require "gapic/operation"
47
+ #
48
+ # op = Gapic::Operation.new(
49
+ # api_client.method_that_returns_longrunning_operation(),
50
+ # operations_client,
51
+ # Google::Example::ResultType,
52
+ # Google::Example::MetadataType
53
+ # )
54
+ #
55
+ # # Register a callback to be run when an operation is done.
56
+ # op.on_done do |operation|
57
+ # raise operation.results.message if operation.error?
58
+ # # process(operation.results)
59
+ # # process(operation.metadata)
60
+ # end
61
+ #
62
+ # # Reload the operation running callbacks if operation completed.
63
+ # op.reload!
64
+ #
65
+ # # Or block until the operation completes, passing a block to be called
66
+ # # on completion.
67
+ # op.wait_until_done do |operation|
68
+ # raise operation.results.message if operation.error?
69
+ # # process(operation.results)
70
+ # # process(operation.rmetadata)
71
+ # end
72
+ #
73
+ # @attribute [r] grpc_op
74
+ # @return [Google::Longrunning::Operation] The wrapped grpc
75
+ # operation object.
76
+ class Operation
77
+ attr_reader :grpc_op
78
+
79
+ ##
80
+ # @param grpc_op [Google::Longrunning::Operation] The inital longrunning operation.
81
+ # @param client [Google::Longrunning::OperationsClient] The client that handles the grpc operations.
82
+ # @param result_type [Class] The class type to be unpacked from the result. If not provided the class type will be
83
+ # looked up. Optional.
84
+ # @param metadata_type [Class] The class type to be unpacked from the metadata. If not provided the class type
85
+ # will be looked up. Optional.
86
+ #
87
+ def initialize grpc_op, client, result_type: nil, metadata_type: nil
88
+ @grpc_op = grpc_op
89
+ @client = client
90
+ @result_type = result_type
91
+ @metadata_type = metadata_type
92
+ @on_done_callbacks = []
93
+ end
94
+
95
+ ##
96
+ # If the operation is done, returns the response. If the operation response is an error, the error will be
97
+ # returned. Otherwise returns nil.
98
+ #
99
+ # @return [Object, Google::Rpc::Status, nil] The result of the operation. If it is an error a
100
+ # {Google::Rpc::Status} will be returned.
101
+ def results
102
+ return error if error?
103
+ return response if response?
104
+ end
105
+
106
+ ##
107
+ # Returns the server-assigned name of the operation, which is only unique within the same service that originally
108
+ # returns it. If you use the default HTTP mapping, the name should have the format of operations/some/unique/name.
109
+ #
110
+ # @return [String] The name of the operation.
111
+ #
112
+ def name
113
+ @grpc_op.name
114
+ end
115
+
116
+ ##
117
+ # Returns the metadata of an operation. If a type is provided, the metadata will be unpacked using the type
118
+ # provided; returning nil if the metadata is not of the type provided. If the type is not of provided, the
119
+ # metadata will be unpacked using the metadata's type_url if the type_url is found in the
120
+ # {Google::Protobuf::DescriptorPool.generated_pool}. If the type cannot be found the raw metadata is retuned.
121
+ #
122
+ # @return [Object, nil] The metadata of the operation. Can be nil.
123
+ #
124
+ def metadata
125
+ return if @grpc_op.metadata.nil?
126
+
127
+ return @grpc_op.metadata.unpack @metadata_type if @metadata_type
128
+
129
+ descriptor = Google::Protobuf::DescriptorPool.generated_pool.lookup @grpc_op.metadata.type_name
130
+
131
+ return @grpc_op.metadata.unpack descriptor.msgclass if descriptor
132
+
133
+ @grpc_op.metadata
134
+ end
135
+
136
+ ##
137
+ # Checks if the operation is done. This does not send a new api call, but checks the result of the previous api
138
+ # call to see if done.
139
+ #
140
+ # @return [Boolean] Whether the operation is done.
141
+ #
142
+ def done?
143
+ @grpc_op.done
144
+ end
145
+
146
+ ##
147
+ # Checks if the operation is done and the result is a response. If the operation is not finished then this will
148
+ # return false.
149
+ #
150
+ # @return [Boolean] Whether a response has been returned.
151
+ #
152
+ def response?
153
+ done? ? @grpc_op.result == :response : false
154
+ end
155
+
156
+ ##
157
+ # If the operation is done, returns the response, otherwise returns nil.
158
+ #
159
+ # @return [Object, nil] The response of the operation.
160
+ def response
161
+ return unless response?
162
+
163
+ return @grpc_op.response.unpack @result_type if @result_type
164
+
165
+ descriptor = Google::Protobuf::DescriptorPool.generated_pool.lookup @grpc_op.response.type_name
166
+
167
+ return @grpc_op.response.unpack descriptor.msgclass if descriptor
168
+
169
+ @grpc_op.response
170
+ end
171
+
172
+ ##
173
+ # Checks if the operation is done and the result is an error. If the operation is not finished then this will
174
+ # return false.
175
+ #
176
+ # @return [Boolean] Whether an error has been returned.
177
+ #
178
+ def error?
179
+ done? ? @grpc_op.result == :error : false
180
+ end
181
+
182
+ ##
183
+ # If the operation response is an error, the error will be returned, otherwise returns nil.
184
+ #
185
+ # @return [Google::Rpc::Status, nil] The error object.
186
+ #
187
+ def error
188
+ @grpc_op.error if error?
189
+ end
190
+
191
+ ##
192
+ # Cancels the operation.
193
+ #
194
+ # @param options [Gapic::CallOptions, Hash] The options for making the RPC call. A Hash can be provided to customize
195
+ # the options object, using keys that match the arguments for {Gapic::CallOptions.new}.
196
+ #
197
+ def cancel options: nil
198
+ # Converts hash and nil to an options object
199
+ options = Gapic::CallOptions.new options.to_h if options.respond_to? :to_h
200
+
201
+ @client.cancel_operation({ name: @grpc_op.name }, options)
202
+ end
203
+
204
+ ##
205
+ # Deletes the operation.
206
+ #
207
+ # @param options [Gapic::CallOptions, Hash] The options for making the RPC call. A Hash can be provided to customize
208
+ # the options object, using keys that match the arguments for {Gapic::CallOptions.new}.
209
+ #
210
+ def delete options: nil
211
+ # Converts hash and nil to an options object
212
+ options = Gapic::CallOptions.new options.to_h if options.respond_to? :to_h
213
+
214
+ @client.delete_operation({ name: @grpc_op.name }, options)
215
+ end
216
+
217
+ ##
218
+ # Reloads the operation object.
219
+ #
220
+ # @param options [Gapic::CallOptions, Hash] The options for making the RPC call. A Hash can be provided to customize
221
+ # the options object, using keys that match the arguments for {Gapic::CallOptions.new}.
222
+ #
223
+ # @return [Gapic::Operation] Since this method changes internal state, it returns itself.
224
+ #
225
+ def reload! options: nil
226
+ # Converts hash and nil to an options object
227
+ options = Gapic::CallOptions.new options.to_h if options.respond_to? :to_h
228
+
229
+ gax_op = @client.get_operation({ name: @grpc_op.name }, options)
230
+ @grpc_op = gax_op.grpc_op
231
+
232
+ if done?
233
+ @on_done_callbacks.each { |proc| proc.call self }
234
+ @on_done_callbacks.clear
235
+ end
236
+
237
+ self
238
+ end
239
+ alias refresh! reload!
240
+
241
+ ##
242
+ # Blocking method to wait until the operation has completed or the maximum timeout has been reached. Upon
243
+ # completion, registered callbacks will be called, then - if a block is given - the block will be called.
244
+ #
245
+ # @param retry_policy [RetryPolicy, Hash, Proc] The policy for retry. A custom proc that takes the error as an
246
+ # argument and blocks can also be provided.
247
+ #
248
+ # @yield operation [Gapic::Operation] Yields the finished Operation.
249
+ #
250
+ def wait_until_done! retry_policy: nil
251
+ retry_policy = RetryPolicy.new retry_policy if retry_policy.is_a? Hash
252
+ retry_policy ||= RetryPolicy.new
253
+
254
+ until done?
255
+ reload!
256
+ break unless retry_policy.call
257
+ end
258
+
259
+ yield self if block_given?
260
+
261
+ self
262
+ end
263
+
264
+ ##
265
+ # Registers a callback to be run when a refreshed operation is marked as done. If the operation has completed
266
+ # prior to a call to this function the callback will be called instead of registered.
267
+ #
268
+ # @yield operation [Gapic::Operation] Yields the finished Operation.
269
+ #
270
+ def on_done &block
271
+ if done?
272
+ yield self
273
+ else
274
+ @on_done_callbacks.push block
275
+ end
276
+ end
277
+ end
278
+ end
@@ -0,0 +1,92 @@
1
+ # Copyright 2019 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
+ module Gapic
16
+ class Operation
17
+ ##
18
+ # The policy for retrying operation reloads using an incremental backoff. A new object instance should be used for
19
+ # every Operation invocation.
20
+ #
21
+ class RetryPolicy
22
+ ##
23
+ # Create new Operation RetryPolicy.
24
+ #
25
+ # @param initial_delay [Numeric] client-side timeout
26
+ # @param multiplier [Numeric] client-side timeout
27
+ # @param max_delay [Numeric] client-side timeout
28
+ # @param timeout [Numeric] client-side timeout
29
+ #
30
+ def initialize initial_delay: nil, multiplier: nil, max_delay: nil, timeout: nil
31
+ @initial_delay = initial_delay
32
+ @multiplier = multiplier
33
+ @max_delay = max_delay
34
+ @timeout = timeout
35
+ @delay = nil
36
+ end
37
+
38
+ def initial_delay
39
+ @initial_delay || 10
40
+ end
41
+
42
+ def multiplier
43
+ @multiplier || 1.3
44
+ end
45
+
46
+ def max_delay
47
+ @max_delay || 300 # Five minutes
48
+ end
49
+
50
+ def timeout
51
+ @timeout || 3600 # One hour
52
+ end
53
+
54
+ def call
55
+ return unless retry?
56
+
57
+ delay!
58
+ increment_delay!
59
+
60
+ true
61
+ end
62
+
63
+ private
64
+
65
+ def deadline
66
+ # memoize the deadline
67
+ @deadline ||= Time.now + timeout
68
+ end
69
+
70
+ def retry?
71
+ deadline > Time.now
72
+ end
73
+
74
+ ##
75
+ # The current delay value.
76
+ def delay
77
+ @delay || initial_delay
78
+ end
79
+
80
+ def delay!
81
+ # Call Kernel.sleep so we can stub it.
82
+ Kernel.sleep delay
83
+ end
84
+
85
+ ##
86
+ # Calculate and set the next delay value.
87
+ def increment_delay!
88
+ @delay = [delay * multiplier, max_delay].min
89
+ end
90
+ end
91
+ end
92
+ end