gapic-common 0.6.1 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e36d6760d01349ef15250fe390b8cc0414933810f1cceccf1a7eeb3b4e08cec7
4
- data.tar.gz: 12873f51efd0b4eb733ca1487d06f6cb58fd4a5bfae3bb4d4921a717ddc41de4
3
+ metadata.gz: 4187157fccf115d742dc9e5b074feccf9b65a5dc241bab8214810b4c257288a8
4
+ data.tar.gz: 7160ec37659fd5bbffc3e517bd40a65295da42fad7743b38869b58d59a34b09f
5
5
  SHA512:
6
- metadata.gz: 60ff219975cb030aa4cc30fb991723725895c2af9aa3c26774f4b5942c5ebe98c48e4ba22b3dd43364446018cb8b831bb0bfbdf1440827dba614cf2b4c2d8c0f
7
- data.tar.gz: 52bf0bb0d636441ab6cb8a7bff28548bb0a595f52f7fe998ed75afefedf8bb28edece71c95c57775caa0d08e5fdcd9cf2dc43da1c7eec81b94d670f17bb88fbf
6
+ metadata.gz: 71cdc0f8c19c10a51dee060c3048b8b13b8223c0e527e103629058504b60013180e24232ae729014943e1d4b66ba98563796f35558d0d2b5fd955a49f0abf720
7
+ data.tar.gz: 31b53d85d36746f58b17e778f8fd4b5469a782ae9a68c3847eee309dcb5b2b4ae068785e295763ac17d081518656427408142426cfa7b0f128287599df3e2c47
data/CHANGELOG.md CHANGED
@@ -1,8 +1,21 @@
1
1
  # Release History
2
2
 
3
- ### 0.6.1 / 2022-04-28
3
+ ### 0.9.0 (2022-05-18)
4
4
 
5
- * Fixed permissions for some files
5
+ #### Features
6
+
7
+ * add full grpc transcoding to gapic-common
8
+ #### Bug Fixes
9
+
10
+ * small fixes for combined libraries and testing
11
+
12
+ ### 0.8.0 / 2022-01-20
13
+
14
+ * Add generic LROs helpers. These are used for the Nonstandard (not conforming to AIP-151) Cloud LROs.
15
+
16
+ ### 0.7.0 / 2021-08-03
17
+
18
+ * Require googleauth 0.17 for proper support of JWT credentials with custom scopes
6
19
 
7
20
  ### 0.6.0 / 2021-07-22
8
21
 
data/README.md CHANGED
@@ -28,7 +28,7 @@ about the Ruby support schedule.
28
28
 
29
29
  Contributions to this library are always welcome and highly encouraged.
30
30
 
31
- See the [CONTRIBUTING](CONTRIBUTING.md) documentation for more information on how to get started.
31
+ See the {file:CONTRIBUTING.md CONTRIBUTING} documentation for more information on how to get started.
32
32
 
33
33
  ## Versioning
34
34
 
@@ -0,0 +1,23 @@
1
+ # Copyright 2022 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 "json"
16
+
17
+ module Gapic
18
+ module Common
19
+ # Gapic Common exception class
20
+ class Error < StandardError
21
+ end
22
+ end
23
+ end
@@ -14,6 +14,6 @@
14
14
 
15
15
  module Gapic
16
16
  module Common
17
- VERSION = "0.6.1".freeze
17
+ VERSION = "0.9.0".freeze
18
18
  end
19
19
  end
data/lib/gapic/common.rb CHANGED
@@ -15,6 +15,7 @@
15
15
  require "grpc/errors"
16
16
  require "grpc/core/status_codes"
17
17
 
18
+ require "gapic/common/error"
18
19
  require "gapic/call_options"
19
20
  require "gapic/headers"
20
21
  require "gapic/operation"
@@ -0,0 +1,38 @@
1
+ # Copyright 2021 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
+ module GenericLRO
17
+ ##
18
+ # A base class for the wrappers over the long-running operations.
19
+ #
20
+ # @attribute [r] operation
21
+ # @return [Object] The wrapped operation object.
22
+ #
23
+ class BaseOperation
24
+ attr_reader :operation
25
+
26
+ ##
27
+ # @private
28
+ # @param operation [Object] The operation object to be wrapped
29
+ def initialize operation
30
+ @operation = operation
31
+ end
32
+
33
+ protected
34
+
35
+ attr_writer :operation
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,325 @@
1
+ # Copyright 2021 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/generic_lro/base_operation"
16
+ require "gapic/operation/retry_policy"
17
+
18
+ module Gapic
19
+ module GenericLRO
20
+ ##
21
+ # A class used to wrap the longrunning operation objects, including the nonstandard ones
22
+ # (`nonstandard` meaning not conforming to the AIP-151).
23
+ # It provides helper methods to poll and check for status of these operations.
24
+ #
25
+ class Operation < Gapic::GenericLRO::BaseOperation
26
+ ##
27
+ # @param operation [Object] The long-running operation object that is returned by the initial method call.
28
+ #
29
+ # @param client [Object] The client that handles the polling for the longrunning operation.
30
+ #
31
+ # @param polling_method_name [String] The name of the methods on the client that polls the longrunning operation.
32
+ #
33
+ # @param operation_status_field [String] The name of the `status` field in the underlying long-running operation
34
+ # object. The `status` field signals that the operation has finished. It should either contain symbols, and
35
+ # be set to `:DONE` when finished or contain a boolean and be set to `true` when finished.
36
+ #
37
+ # @param request_values [Map<String, String>] The values that are to be copied from the request that
38
+ # triggered the longrunning operation, into the request that polls for the longrunning operation.
39
+ # The format is `name of the request field` -> `value`
40
+ #
41
+ # @param operation_name_field [String, nil] The name of the `name` field in the underlying long-running operation
42
+ # object. Optional.
43
+ #
44
+ # @param operation_err_field [String, nil] The name of the `error` field in the underlying long-running operation
45
+ # object. The `error` field should be a message-type, and have same semantics as `google.rpc.Status`, including
46
+ # an integer `code` subfield, that carries an error code. If the `operation_err_field` field is given,
47
+ # the `operation_err_code_field` and `operation_err_msg_field` parameters are ignored. Optional.
48
+ #
49
+ # @param operation_err_code_field [String, nil] The name of the `error_code` field in the underlying
50
+ # long-running operation object. It is ignored if `operation_err_field` is given. Optional.
51
+ #
52
+ # @param operation_err_msg_field [String, nil] The name of the `error_message` field in the underlying
53
+ # long-running operation object. It is ignored if `operation_err_field` is given. Optional.
54
+ #
55
+ # @param operation_copy_fields [Map<String, String>] The map of the fields that need to be copied from the
56
+ # long-running operation object that the polling method returns to the polling request.
57
+ # The format is `name of the operation object field` -> `name of the request field` (`from` -> `to`)
58
+ #
59
+ # @param options [Gapic::CallOptions] call options for this operation
60
+ #
61
+ def initialize operation, client:, polling_method_name:, operation_status_field:,
62
+ request_values: {}, operation_name_field: nil, operation_err_field: nil,
63
+ operation_err_code_field: nil, operation_err_msg_field: nil, operation_copy_fields: {},
64
+ options: {}
65
+ @client = client
66
+ @polling_method_name = polling_method_name
67
+ @operation_status_field = operation_status_field
68
+
69
+ @request_values = request_values || {}
70
+
71
+ @operation_name_field = operation_name_field
72
+ @operation_err_field = operation_err_field
73
+ @operation_err_code_field = operation_err_code_field
74
+ @operation_err_msg_field = operation_err_msg_field
75
+
76
+ @operation_copy_fields = operation_copy_fields || {}
77
+
78
+ @on_done_callbacks = []
79
+ @on_reload_callbacks = []
80
+ @options = options || {}
81
+
82
+ super operation
83
+ end
84
+
85
+ ##
86
+ # If the operation is done, returns the response. If the operation response is an error, the error will be
87
+ # returned. Otherwise returns nil.
88
+ #
89
+ # @return [Object, nil] The result of the operation or an error.
90
+ #
91
+ def results
92
+ return error if error?
93
+ return response if response?
94
+ end
95
+
96
+ ##
97
+ # Returns the name of the operation, if specified.
98
+ #
99
+ # @return [String, nil] The name of the operation.
100
+ #
101
+ def name
102
+ return nil if @operation_name_field.nil?
103
+ operation.send @operation_name_field if operation.respond_to? @operation_name_field
104
+ end
105
+
106
+ ##
107
+ # Checks if the operation is done. This does not send a new api call, but checks the result of the previous api
108
+ # call to see if done.
109
+ #
110
+ # @return [Boolean] Whether the operation is done.
111
+ #
112
+ def done?
113
+ return status if [true, false].include? status
114
+
115
+ status == :DONE
116
+ end
117
+
118
+ ##
119
+ # Checks if the operation is done and the result is not an error. If the operation is not finished then this will
120
+ # return false.
121
+ #
122
+ # @return [Boolean] Whether a response has been returned.
123
+ #
124
+ def response?
125
+ done? && !error?
126
+ end
127
+
128
+ ##
129
+ # If the operation is completed successfully, returns the underlying operation object, otherwise returns nil.
130
+ #
131
+ # @return [Object, nil] The response of the operation.
132
+ def response
133
+ operation if response?
134
+ end
135
+
136
+ ##
137
+ # Checks if the operation is done and the result is an error. If the operation is not finished then this will
138
+ # return false.
139
+ #
140
+ # @return [Boolean] Whether an error has been returned.
141
+ #
142
+ def error?
143
+ done? && (!err.nil? || !error_code.nil?)
144
+ end
145
+
146
+ ##
147
+ # If the operation response is an error, the error will be returned, otherwise returns nil.
148
+ #
149
+ # @return [Object, nil] The error object.
150
+ #
151
+ def error
152
+ return unless error?
153
+ err || GenericError.new(error_code, error_msg)
154
+ end
155
+
156
+ ##
157
+ # Reloads the operation object.
158
+ #
159
+ # @param options [Gapic::CallOptions, Hash] The options for making the RPC call. A Hash can be provided
160
+ # to customize the options object, using keys that match the arguments for {Gapic::CallOptions.new}.
161
+ #
162
+ # @return [Gapic::GenericLRO::Operation] Since this method changes internal state, it returns itself.
163
+ #
164
+ def reload! options: nil
165
+ return self if done?
166
+
167
+ @on_reload_callbacks.each { |proc| proc.call self }
168
+
169
+ request_hash = @request_values.transform_keys(&:to_sym)
170
+ @operation_copy_fields.each do |field_from, field_to|
171
+ request_hash[field_to.to_sym] = operation.send field_from.to_s if operation.respond_to? field_from.to_s
172
+ end
173
+
174
+ options = merge_options options, @options
175
+
176
+ ops = @client.send @polling_method_name, request_hash, options
177
+ ops = ops.operation if ops.is_a? Gapic::GenericLRO::BaseOperation
178
+
179
+ self.operation = ops
180
+
181
+ if done?
182
+ @on_reload_callbacks.clear
183
+ @on_done_callbacks.each { |proc| proc.call self }
184
+ @on_done_callbacks.clear
185
+ end
186
+
187
+ self
188
+ end
189
+ alias refresh! reload!
190
+
191
+ ##
192
+ # Blocking method to wait until the operation has completed or the maximum timeout has been reached. Upon
193
+ # completion, registered callbacks will be called, then - if a block is given - the block will be called.
194
+ #
195
+ # @param retry_policy [RetryPolicy, Hash, Proc] The policy for retry. A custom proc that takes the error as an
196
+ # argument and blocks can also be provided.
197
+ #
198
+ # @yield operation [Gapic::GenericLRO::Operation] Yields the finished Operation.
199
+ #
200
+ def wait_until_done! retry_policy: nil
201
+ retry_policy = ::Gapic::Operation::RetryPolicy.new retry_policy if retry_policy.is_a? Hash
202
+ retry_policy ||= ::Gapic::Operation::RetryPolicy.new
203
+
204
+ until done?
205
+ reload!
206
+ break unless retry_policy.call
207
+ end
208
+
209
+ yield self if block_given?
210
+
211
+ self
212
+ end
213
+
214
+ ##
215
+ # Registers a callback to be run when an operation is being reloaded. If the operation has completed
216
+ # prior to a call to this function the callback will NOT be called or registered.
217
+ #
218
+ # @yield operation [Gapic::Operation] Yields the finished Operation.
219
+ #
220
+ def on_reload &block
221
+ return if done?
222
+ @on_reload_callbacks.push block
223
+ end
224
+
225
+ ##
226
+ # Registers a callback to be run when a refreshed operation is marked as done. If the operation has completed
227
+ # prior to a call to this function the callback will be called instead of registered.
228
+ #
229
+ # @yield operation [Gapic::Operation] Yields the finished Operation.
230
+ #
231
+ def on_done &block
232
+ if done?
233
+ yield self
234
+ else
235
+ @on_done_callbacks.push block
236
+ end
237
+ end
238
+
239
+ private
240
+
241
+ ##
242
+ # @return [String, Boolean, nil] A status, whether operation is Done,
243
+ # as either a boolean (`true` === Done) or a symbol (`:DONE` === Done)
244
+ #
245
+ def status
246
+ return nil if @operation_status_field.nil?
247
+ operation.send @operation_status_field
248
+ end
249
+
250
+ ##
251
+ # @return [String, nil] An error message if the error message field is specified
252
+ #
253
+ def err
254
+ return nil if @operation_err_field.nil?
255
+ operation.send @operation_err_field if operation.respond_to? @operation_err_field
256
+ end
257
+
258
+ ##
259
+ # @return [String, nil] An error code if the error code field is specified
260
+ #
261
+ def error_code
262
+ return nil if @operation_err_code_field.nil?
263
+ operation.send @operation_err_code_field if operation.respond_to? @operation_err_code_field
264
+ end
265
+
266
+ ##
267
+ # @return [String, nil] An error message if the error message field is specified
268
+ #
269
+ def error_msg
270
+ return nil if @operation_err_msg_field.nil?
271
+ operation.send @operation_err_msg_field if operation.respond_to? @operation_err_msg_field
272
+ end
273
+
274
+ ##
275
+ # Merges options given to the method with a baseline Gapic::Options object
276
+ #
277
+ # @param method_opts [Gapic::CallOptions, Hash] The options for making the RPC call given to a method invocation.
278
+ # A Hash can be provided to customize the options object, using keys that match the arguments
279
+ # for {Gapic::CallOptions.new}.
280
+ #
281
+ # @param baseline_opts [Gapic::CallOptions, Hash] The baseline options for making the RPC call.
282
+ # A Hash can be provided to customize the options object, using keys that match the arguments
283
+ # for {Gapic::CallOptions.new}.
284
+ #
285
+ def merge_options method_opts, baseline_opts
286
+ options = if method_opts.respond_to? :to_h
287
+ method_opts.to_h.merge baseline_opts.to_h
288
+ else
289
+ baseline_opts.to_h
290
+ end
291
+
292
+ Gapic::CallOptions.new(**options)
293
+ end
294
+
295
+ ##
296
+ # Represents a generic error that a generic LRO can report
297
+ #
298
+ # @!attribute [r] code
299
+ # @return [String] An error code
300
+ #
301
+ # @!attribute [r] message
302
+ # @return [String] An error message
303
+ #
304
+ class GenericError
305
+ attr_accessor :code
306
+ attr_accessor :message
307
+
308
+ ##
309
+ # @param code [String] An error code
310
+ # @param message [String] An error message
311
+ def initialize code, message
312
+ @code = code
313
+ @message = message
314
+ end
315
+ end
316
+
317
+ protected
318
+
319
+ ##
320
+ # @private
321
+ # @return [Object] The client that handles the polling for the longrunning operation.
322
+ attr_accessor :client
323
+ end
324
+ end
325
+ end
data/lib/gapic/headers.rb CHANGED
@@ -38,7 +38,7 @@ module Gapic
38
38
 
39
39
  ruby_version ||= ::RUBY_VERSION
40
40
  gax_version ||= ::Gapic::Common::VERSION
41
- grpc_version ||= ::GRPC::VERSION if defined? ::GRPC
41
+ grpc_version ||= ::GRPC::VERSION if defined? ::GRPC::VERSION
42
42
  rest_version ||= ::Faraday::VERSION if defined? ::Faraday
43
43
 
44
44
  x_goog_api_client_header = ["gl-ruby/#{ruby_version}"]
@@ -64,7 +64,7 @@ module Gapic
64
64
  #
65
65
  # # Or block until the operation completes, passing a block to be called
66
66
  # # on completion.
67
- # op.wait_until_done do |operation|
67
+ # op.wait_until_done! do |operation|
68
68
  # raise operation.results.message if operation.error?
69
69
  # # process(operation.results)
70
70
  # # process(operation.rmetadata)
@@ -37,13 +37,13 @@ module Gapic
37
37
  def initialize endpoint:, credentials:
38
38
  @endpoint = endpoint
39
39
  @endpoint = "https://#{endpoint}" unless /^https?:/.match? endpoint
40
- @endpoint.sub! %r{/$}, ""
40
+ @endpoint = @endpoint.sub %r{/$}, ""
41
41
 
42
42
  @credentials = credentials
43
43
 
44
44
  @connection = Faraday.new url: @endpoint do |conn|
45
45
  conn.headers = { "Content-Type" => "application/json" }
46
- conn.request :google_authorization, @credentials
46
+ conn.request :google_authorization, @credentials unless @credentials.is_a? ::Symbol
47
47
  conn.request :retry
48
48
  conn.response :raise_error
49
49
  conn.adapter :net_http
@@ -115,9 +115,8 @@ module Gapic
115
115
  make_http_request :put, uri: uri, body: body, params: params, options: options
116
116
  end
117
117
 
118
- protected
119
-
120
118
  ##
119
+ # @private
121
120
  # Sends a http request via Faraday
122
121
  # @param verb [Symbol] http verb
123
122
  # @param uri [String] uri to send this request to
@@ -13,11 +13,12 @@
13
13
  # limitations under the License.
14
14
 
15
15
  require "json"
16
+ require "gapic/common/error"
16
17
 
17
18
  module Gapic
18
19
  module Rest
19
20
  # Gapic REST exception class
20
- class Error < StandardError
21
+ class Error < ::Gapic::Common::Error
21
22
  # @return [Integer] the http status code for the error
22
23
  attr_reader :status_code
23
24
 
@@ -0,0 +1,66 @@
1
+ # Copyright 2022 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
+ module Rest
17
+ class GrpcTranscoder
18
+ # @private
19
+ # A single binding for GRPC-REST transcoding of a request
20
+ # It includes a uri template with bound field parameters, a HTTP method type,
21
+ # and a body template
22
+ #
23
+ # @attribute [r] method
24
+ # @return [Symbol] The REST verb for the request.
25
+ # @attribute [r] template
26
+ # @return [String] The URI template for the request.
27
+ # @attribute [r] field_bindings
28
+ # @return [Array<FieldBinding>] The field bindings for the URI template variables.
29
+ # @attribute [r] body
30
+ # @return [String] The body template for the request.
31
+ class HttpBinding
32
+ attr_reader :method
33
+ attr_reader :template
34
+ attr_reader :field_bindings
35
+ attr_reader :body
36
+
37
+ def initialize method, template, field_bindings, body
38
+ @method = method
39
+ @template = template
40
+ @field_bindings = field_bindings
41
+ @body = body
42
+ end
43
+
44
+ # A single binding for a field of a request message.
45
+ # @attribute [r] field_path
46
+ # @return [String] The path of the bound field, e.g. `foo.bar`.
47
+ # @attribute [r] regex
48
+ # @return [Regexp] The regex to match on the bound field's string representation.
49
+ # @attribute [r] preserve_slashes
50
+ # @return [Boolean] Whether the slashes in the field value should be preserved
51
+ # (as opposed to percent-escaped)
52
+ class FieldBinding
53
+ attr_reader :field_path
54
+ attr_reader :regex
55
+ attr_reader :preserve_slashes
56
+
57
+ def initialize field_path, regex, preserve_slashes
58
+ @field_path = field_path
59
+ @regex = regex
60
+ @preserve_slashes = preserve_slashes
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,298 @@
1
+ # Copyright 2022 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/rest/grpc_transcoder/http_binding"
16
+
17
+ module Gapic
18
+ module Rest
19
+ # @private
20
+ # Transcodes a proto request message into HTTP Rest call components
21
+ # using a configuration of bindings.
22
+ # Internal doc go/actools-regapic-grpc-transcoding.
23
+ class GrpcTranscoder
24
+ def initialize bindings = nil
25
+ @bindings = bindings || []
26
+ end
27
+
28
+ ##
29
+ # @private
30
+ # Creates a new trascoder that is a copy of this one, but with an additional
31
+ # binding defined by the parameters.
32
+ #
33
+ # @param uri_method [Symbol] The rest verb for the binding.
34
+ # @param uri_template [String] The string with uri template for the binding.
35
+ # This string will be expanded with the parameters from variable bindings.
36
+ # @param matches [Array<Array>] Variable bindings in an array. Every element
37
+ # of the array is an [Array] triplet, where:
38
+ # - the first element is a [String] field path (e.g. `foo.bar`) in the request
39
+ # to bind to
40
+ # - the second element is a [Regexp] to match the field value
41
+ # - the third element is a [Boolean] whether the slashes in the field value
42
+ # should be preserved (as opposed to escaped) when expanding the uri template.
43
+ # @param body [String, Nil] The body template, e.g. `*` or a field path.
44
+ #
45
+ # @return [Gapic::Rest::GrpcTranscoder] The updated transcoder.
46
+ def with_bindings uri_method:, uri_template:, matches: [], body: nil
47
+ template = uri_template
48
+
49
+ matches.each do |name, _regex, _preserve_slashes|
50
+ unless uri_template =~ /({#{Regexp.quote name}})/
51
+ err_msg = "Binding configuration is incorrect: missing parameter in the URI template.\n" \
52
+ "Parameter `#{name}` is specified for matching but there is no corresponding parameter" \
53
+ " `{#{name}}` in the URI template."
54
+ raise ::Gapic::Common::Error, err_msg
55
+ end
56
+
57
+ template = template.gsub "{#{name}}", ""
58
+ end
59
+
60
+ if template =~ /{([a-zA-Z_.]+)}/
61
+ err_name = Regexp.last_match[1]
62
+ err_msg = "Binding configuration is incorrect: missing match configuration.\n" \
63
+ "Parameter `{#{err_name}}` is specified in the URI template but there is no" \
64
+ " corresponding match configuration for `#{err_name}`."
65
+ raise ::Gapic::Common::Error, err_msg
66
+ end
67
+
68
+ if body&.include? "."
69
+ raise ::Gapic::Common::Error,
70
+ "Provided body template `#{body}` points to a field in a sub-message. This is not supported."
71
+ end
72
+
73
+ field_bindings = matches.map do |name, regex, preserve_slashes|
74
+ HttpBinding::FieldBinding.new name, regex, preserve_slashes
75
+ end
76
+ GrpcTranscoder.new @bindings + [HttpBinding.new(uri_method, uri_template, field_bindings, body)]
77
+ end
78
+
79
+ ##
80
+ # @private
81
+ # Performs the full grpc transcoding -- creating a REST request from the GRPC request
82
+ # by matching the http bindings and choosing the last one to match.
83
+ # From the matching binding and the request the following components of the REST request
84
+ # are produced:
85
+ # - A [Symbol] representing the Rest verb (e.g. `:get`)
86
+ # - Uri [String] (e.g. `books/100:read`)
87
+ # - Query string params in the form of key-value pairs [Array<Array{String, String}>]
88
+ # (e.g. [["foo", "bar"], ["baz", "qux"]])
89
+ # - Body of the request [String]
90
+ #
91
+ # @param request [Object] The GRPC request object
92
+ #
93
+ # @return [Array] The components of the transcoded request.
94
+ def transcode request
95
+ # Using bindings in reverse here because of the "last one wins" rule
96
+ @bindings.reverse.each do |http_binding|
97
+ # The main reason we are using request.to_json here
98
+ # is that the unset proto3_optional fields will not be
99
+ # in that JSON, letting us skip the checks that would look like
100
+ # `request.respond_to?("has_#{key}?".to_sym) && !request.send("has_#{key}?".to_sym)`
101
+ # The reason we set emit_defaults: true is to avoid
102
+ # having to figure out default values for the required
103
+ # fields at a runtime.
104
+ #
105
+ # Make a new one for each binding because extract_scalar_value! is destructive
106
+ request_hash = JSON.parse request.to_json emit_defaults: true
107
+
108
+ uri_values = bind_uri_values! http_binding, request_hash
109
+ next if uri_values.any? { |_, value| value.nil? }
110
+
111
+ if http_binding.body && http_binding.body != "*"
112
+ # Note that the body template can only point to a top-level field,
113
+ # so there is no need to split the path.
114
+ body_binding_camel = camel_name_for http_binding.body
115
+ next unless request_hash.key? body_binding_camel
116
+ end
117
+
118
+ method = http_binding.method
119
+ uri = expand_template http_binding.template, uri_values
120
+ body, query_params = construct_body_query_params http_binding.body, request_hash, request
121
+
122
+ return method, uri, query_params, body
123
+ end
124
+
125
+ raise ::Gapic::Common::Error,
126
+ "Request object does not match any transcoding template. Cannot form a correct REST call."
127
+ end
128
+
129
+ private
130
+
131
+ # Binds request values for the uri template expansion.
132
+ # This method modifies the provided `request_hash` parameter.
133
+ # Returned values are percent-escaped with slashes potentially preserved.
134
+ # @param http_binding [Gapic::Rest::GrpcTranscoder::HttpBinding]
135
+ # Http binding to get the field bindings from.
136
+ # @param request_hash [Hash]
137
+ # A hash of the GRPC request with the unset proto3_optional fields pre-removed.
138
+ # !!! This hash will be modified. The bound fields will be deleted. !!!
139
+ # @return [Hash{String, String}]
140
+ # Name to value hash of the variables for the uri template expansion.
141
+ # The values are percent-escaped with slashes potentially preserved.
142
+ def bind_uri_values! http_binding, request_hash
143
+ http_binding.field_bindings.map do |field_binding|
144
+ field_path_camel = field_binding.field_path.split(".").map { |part| camel_name_for part }.join(".")
145
+ field_value = extract_scalar_value! request_hash, field_path_camel, field_binding.regex
146
+
147
+ if field_value
148
+ field_value = if field_binding.preserve_slashes
149
+ field_value.split("/").map { |segment| percent_escape(segment) }.join("/")
150
+ else
151
+ percent_escape field_value
152
+ end
153
+ end
154
+
155
+ [field_binding.field_path, field_value]
156
+ end.to_h
157
+ end
158
+
159
+ # Percent-escapes a string.
160
+ # @param str [String] String to escape.
161
+ # @return str [String] Escaped string.
162
+ def percent_escape str
163
+ # `+` to represent spaces is not currently supported in Showcase server.
164
+ CGI.escape(str).gsub("+", "%20")
165
+ end
166
+
167
+ # Constructs body and query parameters for the Rest request.
168
+ # @param body_template [String, Nil] The template for the body, e.g. `*`.
169
+ # @param request_hash_without_uri [Hash]
170
+ # The hash of the GRPC request with the unset proto3_optional fields
171
+ # and the values that are bound to URI removed.
172
+ # @param request [Object] The GRPC request.
173
+ # @return [Array{String, Array}] A pair of body and query parameters.
174
+ def construct_body_query_params body_template, request_hash_without_uri, request
175
+ body = ""
176
+ query_params = []
177
+
178
+ if body_template == "*"
179
+ body = request_hash_without_uri.to_json
180
+ elsif body_template && body_template != ""
181
+ # Using a `request` here instead of `request_hash_without_uri`
182
+ # because if `body` is bound to a message field,
183
+ # the fields of the corresponding sub-message,
184
+ # which were used when constructing the URI, should not be deleted
185
+ # (as opposed to the case when `body` is `*`).
186
+ #
187
+ # The `request_hash_without_uri` at this point was mutated to delete these fields.
188
+ #
189
+ # Note that the body template can only point to a top-level field
190
+ request_hash_without_uri.delete camel_name_for body_template
191
+ body = request.send(body_template.to_sym).to_json(emit_defaults: true)
192
+ query_params = build_query_params request_hash_without_uri
193
+ else
194
+ query_params = build_query_params request_hash_without_uri
195
+ end
196
+
197
+ [body, query_params]
198
+ end
199
+
200
+ # Builds query params for the REST request.
201
+ # This function calls itself recursively for every submessage field, passing
202
+ # the submessage hash as request and the path to the submessage field as a prefix.
203
+ # @param request_hash [Hash]
204
+ # A hash of the GRPC request or the sub-request with the unset
205
+ # proto3_optional fields and the values that are bound to URI removed.
206
+ # @param prefix [String] A prefix to form the correct query parameter key.
207
+ # @return [Array{String, String}] Query string params as key-value pairs.
208
+ def build_query_params request_hash, prefix = ""
209
+ result = []
210
+ request_hash.each do |key, value|
211
+ full_key_name = "#{prefix}#{key}"
212
+ case value
213
+ when ::Array
214
+ value.each do |_val|
215
+ result.push "#{full_key_name}=#{value}"
216
+ end
217
+ when ::Hash
218
+ result += build_query_params value, "#{full_key_name}."
219
+ else
220
+ result.push "#{full_key_name}=#{value}" unless value.nil?
221
+ end
222
+ end
223
+
224
+ result
225
+ end
226
+
227
+ # Extracts a non-submessage non-array value from the request hash by path
228
+ # if its string representation matches the regex provided.
229
+ # This method modifies the provided `request_hash` parameter.
230
+ # Returns nil if:
231
+ # - the field is not found
232
+ # - the field is a Message or an array,
233
+ # - the regex does not match
234
+ # @param request_hash [Hash]
235
+ # A hash of the GRPC request or the sub-request with the unset
236
+ # proto3_optional fields removed.
237
+ # !!! This hash will be modified. The extracted field will be deleted. !!!
238
+ # @param field_path [String] A path to the field, e.g. `foo.bar`.
239
+ # @param regex [Regexp] A regex to match on the field's string representation.
240
+ # @return [String, Nil] the field's string representation or nil.
241
+ def extract_scalar_value! request_hash, field_path, regex
242
+ parent, name = find_value request_hash, field_path
243
+ value = parent.delete name
244
+
245
+ # Covers the case where in `foo.bar.baz`, `baz` is still a submessage or an array.
246
+ return nil if value.is_a?(::Hash) || value.is_a?(::Array)
247
+ return value.to_s if value.to_s =~ regex
248
+ end
249
+
250
+ # Finds a value in the hash by path.
251
+ # @param request_hash [Hash] A hash of the GRPC request or the sub-request.
252
+ # @param field_path [String] A path of the field, e.g. `foo.bar`.
253
+ def find_value request_hash, field_path
254
+ path_split = field_path.split "."
255
+
256
+ value_parent = nil
257
+ value = request_hash
258
+ last_field_name = nil
259
+ path_split.each do |curr_field|
260
+ # Covers the case when in `foo.bar.baz`, `bar` is not a submessage field
261
+ # or is a submessage field initialized with nil.
262
+ return {}, nil unless value.is_a? ::Hash
263
+ value_parent = value
264
+ last_field_name = curr_field
265
+ value = value[curr_field]
266
+ end
267
+
268
+ [value_parent, last_field_name]
269
+ end
270
+
271
+ # Performs variable expansion on the template using the bindings provided
272
+ # @param template [String] The Uri template.
273
+ # @param bindings [Hash{String, String}]
274
+ # The variable bindings. The values should be percent-escaped
275
+ # (with slashes potentially preserved).
276
+ # @return [String] The expanded template.
277
+ def expand_template template, bindings
278
+ result = template
279
+ bindings.each do |name, value|
280
+ result = result.gsub "{#{name}}", value
281
+ end
282
+ result
283
+ end
284
+
285
+ ##
286
+ # Converts a snake_case parameter name into camelCase for query string parameters.
287
+ # @param attr_name [String] Parameter name.
288
+ # @return [String] Camel-cased parameter name.
289
+ def camel_name_for attr_name
290
+ parts = attr_name.split "_"
291
+ first_part = parts[0]
292
+ other_parts = parts[1..-1]
293
+ other_parts_pascal = other_parts.map(&:capitalize).join
294
+ "#{first_part}#{other_parts_pascal}"
295
+ end
296
+ end
297
+ end
298
+ end
@@ -12,22 +12,14 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
+ require "gapic/generic_lro/base_operation"
16
+
15
17
  module Gapic
16
18
  module Rest
17
19
  ##
18
- # A base class for the wrappers over the long-running operations to the response of a REST LRO method.
19
- #
20
- # @attribute [r] operation
21
- # @return [Object] The wrapped operation object.
22
- class BaseOperation
23
- attr_reader :operation
24
-
25
- ##
26
- # @private
27
- # @param operation [Object] The wrapped operation object.
28
- def initialize operation
29
- @operation = operation
30
- end
31
- end
20
+ # This alias is left here for the backwards compatibility purposes.
21
+ # Rest LROs now use the same GenericLRO base as the GRPC LROs.
22
+ # @deprecated Use {Gapic::GenericLRO::BaseOperation} instead.
23
+ BaseOperation = Gapic::GenericLRO::BaseOperation
32
24
  end
33
25
  end
data/lib/gapic/rest.rb CHANGED
@@ -24,6 +24,7 @@ require "gapic/protobuf"
24
24
  require "gapic/rest/client_stub"
25
25
  require "gapic/rest/error"
26
26
  require "gapic/rest/faraday_middleware"
27
+ require "gapic/rest/grpc_transcoder"
27
28
  require "gapic/rest/operation"
28
29
  require "gapic/rest/paged_enumerable"
29
30
  require "json"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gapic-common
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.1
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Google API Authors
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-04-28 00:00:00.000000000 Z
11
+ date: 2022-05-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -70,7 +70,7 @@ dependencies:
70
70
  requirements:
71
71
  - - ">="
72
72
  - !ruby/object:Gem::Version
73
- version: 0.16.2
73
+ version: 0.17.0
74
74
  - - "<"
75
75
  - !ruby/object:Gem::Version
76
76
  version: 2.a
@@ -80,7 +80,7 @@ dependencies:
80
80
  requirements:
81
81
  - - ">="
82
82
  - !ruby/object:Gem::Version
83
- version: 0.16.2
83
+ version: 0.17.0
84
84
  - - "<"
85
85
  - !ruby/object:Gem::Version
86
86
  version: 2.a
@@ -196,6 +196,20 @@ dependencies:
196
196
  - - "~>"
197
197
  - !ruby/object:Gem::Version
198
198
  version: '5.2'
199
+ - !ruby/object:Gem::Dependency
200
+ name: pry
201
+ requirement: !ruby/object:Gem::Requirement
202
+ requirements:
203
+ - - ">="
204
+ - !ruby/object:Gem::Version
205
+ version: '0.14'
206
+ type: :development
207
+ prerelease: false
208
+ version_requirements: !ruby/object:Gem::Requirement
209
+ requirements:
210
+ - - ">="
211
+ - !ruby/object:Gem::Version
212
+ version: '0.14'
199
213
  - !ruby/object:Gem::Dependency
200
214
  name: rake
201
215
  requirement: !ruby/object:Gem::Requirement
@@ -256,9 +270,12 @@ files:
256
270
  - lib/gapic/call_options.rb
257
271
  - lib/gapic/call_options/retry_policy.rb
258
272
  - lib/gapic/common.rb
273
+ - lib/gapic/common/error.rb
259
274
  - lib/gapic/common/version.rb
260
275
  - lib/gapic/config.rb
261
276
  - lib/gapic/config/method.rb
277
+ - lib/gapic/generic_lro/base_operation.rb
278
+ - lib/gapic/generic_lro/operation.rb
262
279
  - lib/gapic/grpc.rb
263
280
  - lib/gapic/grpc/service_stub.rb
264
281
  - lib/gapic/grpc/service_stub/rpc_call.rb
@@ -272,6 +289,8 @@ files:
272
289
  - lib/gapic/rest/client_stub.rb
273
290
  - lib/gapic/rest/error.rb
274
291
  - lib/gapic/rest/faraday_middleware.rb
292
+ - lib/gapic/rest/grpc_transcoder.rb
293
+ - lib/gapic/rest/grpc_transcoder/http_binding.rb
275
294
  - lib/gapic/rest/operation.rb
276
295
  - lib/gapic/rest/paged_enumerable.rb
277
296
  - lib/gapic/stream_input.rb
@@ -294,7 +313,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
294
313
  - !ruby/object:Gem::Version
295
314
  version: '0'
296
315
  requirements: []
297
- rubygems_version: 3.0.3.1
316
+ rubygems_version: 3.3.5
298
317
  signing_key:
299
318
  specification_version: 4
300
319
  summary: Common code for GAPIC-generated API clients