gapic-common 0.18.0 → 0.20.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: 609c468f1e8a7e60f8b4ba911901dd99f8816cb367371dd5479d79dcd06bca82
4
- data.tar.gz: 23a01faa58af17577e262852d434a6746b33f090247b05ea9dc8ce2ae07772bb
3
+ metadata.gz: dd54dc9471821e191b81b38efd0e03c24cfaaad13093b7b20d174a7235db9408
4
+ data.tar.gz: dda7b540eedb2fb8f665548a6844b6c98c6b5e0b601d4627be16d229cdc1b65f
5
5
  SHA512:
6
- metadata.gz: 7fc2a7f8c0fdcc79434966a1e6431c29d957dd1a646cae5a8cd023da1320b7f5253b69f4edd915c98656b12e681aac66bf11713c2bb47f0482798601635ba948
7
- data.tar.gz: 49a6c6aaa7fca24979d8707bc7a6ba2b8daed51951539d2235be32f932e73a02fa4cd8d7699f811b358bd216ca99ffbe63dab0469196b1815cdf37eb6ad4055c
6
+ metadata.gz: cedbf49ae436a114a347723bda04637fee02481d7a56624f80f79ab1e2903ccc290a7667e1584d11c66bc0b60295333315f99ac15d01806c1713171ed3179dde
7
+ data.tar.gz: a4f733db0e1bb0e158d982fe189dc510195e24299fa8880255917ae7a68b2a2d47ff740bf304fd187678c2ace7c9a75cdabf341347e0f0066fe16e40e7e8d0be
data/CHANGELOG.md CHANGED
@@ -1,5 +1,27 @@
1
1
  # Release History
2
2
 
3
+ ### 0.20.0 (2023-08-31)
4
+
5
+ #### Features
6
+
7
+ * Add channel pooling for gapic clients ([#969](https://github.com/googleapis/gapic-generator-ruby/issues/969))
8
+ * Add LRU hash ([#970](https://github.com/googleapis/gapic-generator-ruby/issues/970))
9
+ #### Documentation
10
+
11
+ * Minor fixes to YARD documentation links and parameters ([#959](https://github.com/googleapis/gapic-generator-ruby/issues/959))
12
+
13
+ ### 0.19.1 (2023-05-30)
14
+
15
+ #### Bug Fixes
16
+
17
+ * Fixed handling of optional fields in coerce ([#954](https://github.com/googleapis/gapic-generator-ruby/issues/954))
18
+
19
+ ### 0.19.0 (2023-05-26)
20
+
21
+ #### Features
22
+
23
+ * Compatibility with protobuf v23 generated map fields ([#948](https://github.com/googleapis/gapic-generator-ruby/issues/948))
24
+
3
25
  ### 0.18.0 (2023-02-27)
4
26
 
5
27
  #### Features
@@ -37,7 +37,7 @@ module Gapic
37
37
  # @param timeout [Numeric] The client-side timeout for RPC calls.
38
38
  # @param metadata [Hash] The request header params.
39
39
  # @param retry_policy [Hash, RetryPolicy, Proc] The policy for error retry. A Hash can be provided to
40
- # customize the policy object, using keys that match the arguments for {RetryPolicy.new}.
40
+ # customize the policy object, using keys that match the arguments for {RetryPolicy.initialize}.
41
41
  #
42
42
  # A Proc object can also be provided. The Proc should accept an error as an argument, and return `true` if the
43
43
  # error should be retried or `false` if not. If the error is to be retried, the Proc object must also block
@@ -59,9 +59,8 @@ module Gapic
59
59
  #
60
60
  # @param timeout [Numeric] The client-side timeout for RPC calls.
61
61
  # @param metadata [Hash] the request header params.
62
- # @param retry_policy [Hash] the policy for error retry.
63
62
  # @param retry_policy [Hash] The policy for error retry. keys must match the arguments for
64
- # {RetryPolicy.new}.
63
+ # {RetryPolicy.initialize}.
65
64
  #
66
65
  def apply_defaults timeout: nil, metadata: nil, retry_policy: nil
67
66
  @timeout ||= timeout
@@ -14,6 +14,6 @@
14
14
 
15
15
  module Gapic
16
16
  module Common
17
- VERSION = "0.18.0".freeze
17
+ VERSION = "0.20.0".freeze
18
18
  end
19
19
  end
data/lib/gapic/config.rb CHANGED
@@ -44,9 +44,9 @@ module Gapic
44
44
  ##
45
45
  # Add configuration attribute methods to the configuratin class.
46
46
  #
47
- # @param [String, Symbol] key The name of the option
48
- # @param [Object, nil] initial Initial value (nil is allowed)
49
- # @param [Hash] opts Validation options
47
+ # @param [String, Symbol] name The name of the option
48
+ # @param [Object, nil] default Initial value (nil is allowed)
49
+ # @param [Array] valid_values A list of valid types
50
50
  #
51
51
  def config_attr name, default, *valid_values, &validator
52
52
  name = String(name).to_sym
@@ -90,7 +90,7 @@ module Gapic
90
90
  #
91
91
  def results
92
92
  return error if error?
93
- return response if response?
93
+ response if response?
94
94
  end
95
95
 
96
96
  ##
@@ -196,7 +196,7 @@ module Gapic
196
196
  # @param retry_policy [RetryPolicy, Hash, Proc] The policy for retry. A custom proc that takes the error as an
197
197
  # argument and blocks can also be provided.
198
198
  #
199
- # @yield operation [Gapic::GenericLRO::Operation] Yields the finished Operation.
199
+ # @yieldparam operation [Gapic::GenericLRO::Operation] Yields the finished Operation.
200
200
  #
201
201
  def wait_until_done! retry_policy: nil
202
202
  retry_policy = ::Gapic::Operation::RetryPolicy.new(**retry_policy) if retry_policy.is_a? Hash
@@ -216,7 +216,7 @@ module Gapic
216
216
  # Registers a callback to be run when an operation is being reloaded. If the operation has completed
217
217
  # prior to a call to this function the callback will NOT be called or registered.
218
218
  #
219
- # @yield operation [Gapic::Operation] Yields the finished Operation.
219
+ # @yieldparam operation [Gapic::Operation] Yields the finished Operation.
220
220
  #
221
221
  def on_reload &block
222
222
  return if done?
@@ -227,7 +227,7 @@ module Gapic
227
227
  # Registers a callback to be run when a refreshed operation is marked as done. If the operation has completed
228
228
  # prior to a call to this function the callback will be called instead of registered.
229
229
  #
230
- # @yield operation [Gapic::Operation] Yields the finished Operation.
230
+ # @yieldparam operation [Gapic::Operation] Yields the finished Operation.
231
231
  #
232
232
  def on_done &block
233
233
  if done?
@@ -0,0 +1,102 @@
1
+ # Copyright 2023 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 "googleauth"
17
+ require "gapic/grpc/service_stub/rpc_call"
18
+
19
+ module Gapic
20
+ class ServiceStub
21
+ ##
22
+ # @private
23
+ #
24
+ # Gapic gRPC ServiceStub Channel.
25
+ #
26
+ # This class wraps the gRPC stub object and its RPC methods.
27
+ #
28
+ class Channel
29
+ attr_reader :concurrent_streams
30
+
31
+ ##
32
+ # Creates a new Channel instance
33
+ #
34
+ def initialize grpc_stub_class, endpoint:, credentials:, channel_args: nil, interceptors: nil,
35
+ on_channel_create: nil
36
+ @grpc_stub_class = grpc_stub_class
37
+ @endpoint = endpoint
38
+ @credentials = credentials
39
+ @channel_args = Hash channel_args
40
+ @interceptors = Array interceptors
41
+ @concurrent_streams = 0
42
+ @mutex = Mutex.new
43
+ setup_grpc_stub
44
+ on_channel_create&.call self
45
+ end
46
+
47
+ ##
48
+ # Creates a gRPC stub object
49
+ #
50
+ def setup_grpc_stub
51
+ raise ArgumentError, "grpc_stub_class is required" if @grpc_stub_class.nil?
52
+ raise ArgumentError, "endpoint is required" if @endpoint.nil?
53
+ raise ArgumentError, "credentials is required" if @credentials.nil?
54
+
55
+ @grpc_stub = case @credentials
56
+ when ::GRPC::Core::Channel
57
+ @grpc_stub_class.new @endpoint, nil, channel_override: @credentials, interceptors: @interceptors
58
+ when ::GRPC::Core::ChannelCredentials, Symbol
59
+ @grpc_stub_class.new @endpoint, @credentials, channel_args: @channel_args,
60
+ interceptors: @interceptors
61
+ else
62
+ updater_proc = @credentials.updater_proc if @credentials.respond_to? :updater_proc
63
+ updater_proc ||= @credentials if @credentials.is_a? Proc
64
+ raise ArgumentError, "invalid credentials (#{credentials.class})" if updater_proc.nil?
65
+
66
+ call_creds = ::GRPC::Core::CallCredentials.new updater_proc
67
+ chan_creds = ::GRPC::Core::ChannelCredentials.new.compose call_creds
68
+ @grpc_stub_class.new @endpoint, chan_creds, channel_args: @channel_args,
69
+ interceptors: @interceptors
70
+ end
71
+ end
72
+
73
+ ##
74
+ # Invoke the specified RPC call.
75
+ #
76
+ # @param method_name [Symbol] The RPC method name.
77
+ # @param request [Object] The request object.
78
+ # @param options [Gapic::CallOptions, Hash] The options for making the RPC call. A Hash can be provided to
79
+ # customize the options object, using keys that match the arguments for {Gapic::CallOptions.new}. This object
80
+ # should only be used once.
81
+ #
82
+ # @yield [response, operation] Access the response along with the RPC operation.
83
+ # @yieldparam response [Object] The response object.
84
+ # @yieldparam operation [::GRPC::ActiveCall::Operation] The RPC operation for the response.
85
+ #
86
+ # @return [Object] The response object.
87
+ #
88
+ def call_rpc method_name, request, options: nil, &block
89
+ @mutex.synchronize { @concurrent_streams += 1 }
90
+ begin
91
+ rpc_call = RpcCall.new @grpc_stub.method method_name
92
+ response = rpc_call.call request, options: options, &block
93
+ response
94
+ rescue StandardError => e
95
+ raise e
96
+ ensure
97
+ @mutex.synchronize { @concurrent_streams -= 1 }
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,113 @@
1
+ # Copyright 2023 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 "googleauth"
17
+ require "gapic/config"
18
+
19
+
20
+ module Gapic
21
+ class ServiceStub
22
+ ##
23
+ # @private
24
+ #
25
+ # Gapic gRPC ServiceStub ChannelPool
26
+ #
27
+ # This class wraps multiple channels for sending RPCs.
28
+ #
29
+ class ChannelPool
30
+ ##
31
+ # Initialize an instance of ServiceStub::ChannelPool
32
+ #
33
+ def initialize grpc_stub_class, endpoint:, credentials:, channel_args: nil, interceptors: nil, config: nil
34
+ if credentials.is_a? ::GRPC::Core::Channel
35
+ raise ArgumentError, "Can't create a channel pool with GRPC::Core::Channel as credentials"
36
+ end
37
+
38
+ @grpc_stub_class = grpc_stub_class
39
+ @endpoint = endpoint
40
+ @credentials = credentials
41
+ @channel_args = channel_args
42
+ @interceptors = interceptors
43
+ @config = config || Configuration.new
44
+
45
+ @channels = (1..@config.channel_count).map { create_channel }
46
+ end
47
+
48
+ ##
49
+ # Creates a new channel.
50
+ def create_channel
51
+ Channel.new @grpc_stub_class, endpoint: @endpoint, credentials: @credentials, channel_args: @channel_args,
52
+ interceptors: @interceptors, on_channel_create: @config.on_channel_create
53
+ end
54
+
55
+ ##
56
+ # Invoke the specified RPC call.
57
+ #
58
+ # @param method_name [Symbol] The RPC method name.
59
+ # @param request [Object] The request object.
60
+ # @param options [Gapic::CallOptions, Hash] The options for making the RPC call. A Hash can be provided to
61
+ # customize the options object, using keys that match the arguments for {Gapic::CallOptions.new}. This object
62
+ # should only be used once.
63
+ #
64
+ # @yield [response, operation] Access the response along with the RPC operation.
65
+ # @yieldparam response [Object] The response object.
66
+ # @yieldparam operation [::GRPC::ActiveCall::Operation] The RPC operation for the response.
67
+ #
68
+ # @return [Object] The response object.
69
+ #
70
+ def call_rpc method_name, request, options: nil, &block
71
+ unless @config.channel_selection == :least_loaded
72
+ warn "Invalid channel selection configuration, resorting to least loaded channel"
73
+ end
74
+ channel = least_loaded_channel
75
+ channel.call_rpc method_name, request, options: options, &block
76
+ end
77
+
78
+
79
+ private
80
+
81
+ ##
82
+ # Return the least loaded channel in the pool
83
+ #
84
+ # @return [::Grpc::ServiceStub::Channel]
85
+ #
86
+ def least_loaded_channel
87
+ @channels.min_by(&:concurrent_streams)
88
+ end
89
+
90
+ ##
91
+ # Configuration class for ChannelPool
92
+ #
93
+ # @!attribute [rw] channel_count
94
+ # The number of channels in the channel pool.
95
+ # return [Integer]
96
+ # @!attribute [rw] on_channel_create
97
+ # Proc to run at the end of each channel initialization.
98
+ # Proc is provided ::Gapic::ServiceStub::Channel object as input.
99
+ # return [Proc]
100
+ # @!attribute [rw] channel_selection
101
+ # The algorithm for selecting a channel for an RPC.
102
+ # return [Symbol]
103
+ #
104
+ class Configuration
105
+ extend ::Gapic::Config
106
+
107
+ config_attr :channel_count, 1, ::Integer
108
+ config_attr :on_channel_create, nil, ::Proc
109
+ config_attr :channel_selection, :least_loaded, :least_loaded
110
+ end
111
+ end
112
+ end
113
+ end
@@ -15,18 +15,24 @@
15
15
  require "grpc"
16
16
  require "googleauth"
17
17
  require "gapic/grpc/service_stub/rpc_call"
18
+ require "gapic/grpc/service_stub/channel"
19
+ require "gapic/grpc/service_stub/channel_pool"
20
+
18
21
 
19
22
  module Gapic
20
23
  ##
21
24
  # Gapic gRPC Stub
22
25
  #
23
- # This class wraps the actual gRPC Stub object and it's RPC methods.
26
+ # This class wraps the actual gRPC Stub and ChannelPool objects.
24
27
  #
25
28
  # @!attribute [r] grpc_stub
26
29
  # @return [Object] The instance of the gRPC stub class (`grpc_stub_class`) constructor argument.
30
+ # @!attribute [r] channel_pool
31
+ # @return [Gapic::ServiceStub::ChannelPool] The instance of the ChannelPool class.
27
32
  #
28
33
  class ServiceStub
29
34
  attr_reader :grpc_stub
35
+ attr_reader :channel_pool
30
36
 
31
37
  ##
32
38
  # Creates a Gapic gRPC stub object.
@@ -49,22 +55,49 @@ module Gapic
49
55
  # provided as a `::GRPC::Core::Channel`.)
50
56
  # @param interceptors [Array<::GRPC::ClientInterceptor>] An array of {::GRPC::ClientInterceptor} objects that will
51
57
  # be used for intercepting calls before they are executed Interceptors are an EXPERIMENTAL API.
58
+ # @param channel_pool_config [::Gapic::ServiceStub:ChannelPool::Configuration] The configuration for channel
59
+ # pool. This argument will raise error when `credentials` is provided as a `::GRPC::Core::Channel`.
52
60
  #
53
- def initialize grpc_stub_class, endpoint:, credentials:, channel_args: nil, interceptors: nil
61
+ def initialize grpc_stub_class, endpoint:, credentials:, channel_args: nil,
62
+ interceptors: nil, channel_pool_config: nil
54
63
  raise ArgumentError, "grpc_stub_class is required" if grpc_stub_class.nil?
55
64
  raise ArgumentError, "endpoint is required" if endpoint.nil?
56
65
  raise ArgumentError, "credentials is required" if credentials.nil?
57
66
 
67
+ @channel_pool = nil
68
+ @grpc_stub = nil
58
69
  channel_args = Hash channel_args
59
70
  interceptors = Array interceptors
60
71
 
72
+
73
+ if channel_pool_config && channel_pool_config.channel_count > 1
74
+ create_channel_pool grpc_stub_class, endpoint: endpoint, credentials: credentials,
75
+ channel_args: channel_args, interceptors: interceptors,
76
+ channel_pool_config: channel_pool_config
77
+ else
78
+ create_grpc_stub grpc_stub_class, endpoint: endpoint, credentials: credentials,
79
+ channel_args: channel_args, interceptors: interceptors
80
+ end
81
+ end
82
+
83
+ def create_channel_pool grpc_stub_class, endpoint:, credentials:, channel_args: nil,
84
+ interceptors: nil, channel_pool_config: nil
85
+ if credentials.is_a? ::GRPC::Core::Channel
86
+ raise ArgumentError, "Cannot create a channel pool with GRPC::Core::Channel as credentials"
87
+ end
88
+ @channel_pool = ChannelPool.new grpc_stub_class, endpoint: endpoint, credentials: credentials,
89
+ channel_args: channel_args, interceptors: interceptors,
90
+ config: channel_pool_config
91
+ end
92
+
93
+ def create_grpc_stub grpc_stub_class, endpoint:, credentials:, channel_args: nil, interceptors: nil
61
94
  @grpc_stub = case credentials
62
95
  when ::GRPC::Core::Channel
63
96
  grpc_stub_class.new endpoint, nil, channel_override: credentials,
64
- interceptors: interceptors
97
+ interceptors: interceptors
65
98
  when ::GRPC::Core::ChannelCredentials, Symbol
66
99
  grpc_stub_class.new endpoint, credentials, channel_args: channel_args,
67
- interceptors: interceptors
100
+ interceptors: interceptors
68
101
  else
69
102
  updater_proc = credentials.updater_proc if credentials.respond_to? :updater_proc
70
103
  updater_proc ||= credentials if credentials.is_a? Proc
@@ -73,7 +106,7 @@ module Gapic
73
106
  call_creds = ::GRPC::Core::CallCredentials.new updater_proc
74
107
  chan_creds = ::GRPC::Core::ChannelCredentials.new.compose call_creds
75
108
  grpc_stub_class.new endpoint, chan_creds, channel_args: channel_args,
76
- interceptors: interceptors
109
+ interceptors: interceptors
77
110
  end
78
111
  end
79
112
 
@@ -152,8 +185,12 @@ module Gapic
152
185
  # end
153
186
  #
154
187
  def call_rpc method_name, request, options: nil, &block
155
- rpc_call = RpcCall.new @grpc_stub.method method_name
156
- rpc_call.call request, options: options, &block
188
+ if @channel_pool.nil?
189
+ rpc_call = RpcCall.new @grpc_stub.method method_name
190
+ rpc_call.call request, options: options, &block
191
+ else
192
+ @channel_pool.call_rpc method_name, request, options: options, &block
193
+ end
157
194
  end
158
195
  end
159
196
  end
@@ -0,0 +1,106 @@
1
+ # Copyright 2023 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
+ ##
17
+ # @private
18
+ #
19
+ # Linked list based hash maintaining the order of
20
+ # access/creation of the keys.
21
+ #
22
+ class LruHash
23
+ def initialize size = 1
24
+ raise ArgumentError, "The size of LRU hash can't be < 1" unless size > 1
25
+ @start = nil
26
+ @end = nil
27
+ @size = size
28
+ @cache = {}
29
+ end
30
+
31
+ def get key
32
+ return nil unless @cache.key? key
33
+ node = @cache[key]
34
+ move_to_top node
35
+ node.value
36
+ end
37
+
38
+ def put key, value
39
+ if @cache.key? key
40
+ node = @cache[key]
41
+ node.value = value
42
+ move_to_top node
43
+ else
44
+ remove_tail if @cache.size >= @size
45
+ new_node = Node.new key, value
46
+ insert_at_top new_node
47
+ @cache[key] = new_node
48
+ end
49
+ end
50
+
51
+ private
52
+
53
+ def move_to_top node
54
+ return if node.equal? @start
55
+
56
+ if node.equal? @end
57
+ @end = node.prev
58
+ @end.next = nil
59
+ else
60
+ node.prev.next = node.next
61
+ node.next.prev = node.prev
62
+ end
63
+
64
+ node.prev = nil
65
+ node.next = @start
66
+ @start.prev = node
67
+ @start = node
68
+ end
69
+
70
+ def remove_tail
71
+ @cache.delete @end.key
72
+ @end = @end.prev
73
+ @end.next = nil if @end
74
+ end
75
+
76
+ def insert_at_top node
77
+ if @start.nil?
78
+ @start = node
79
+ @end = node
80
+ else
81
+ node.next = @start
82
+ @start.prev = node
83
+ @start = node
84
+ end
85
+ end
86
+
87
+ ##
88
+ # @private
89
+ #
90
+ # Node class for linked list.
91
+ #
92
+ class Node
93
+ attr_accessor :key
94
+ attr_accessor :value
95
+ attr_accessor :prev
96
+ attr_accessor :next
97
+
98
+ def initialize key, value
99
+ @key = key
100
+ @value = value
101
+ @prev = nil
102
+ @next = nil
103
+ end
104
+ end
105
+ end
106
+ end
@@ -99,10 +99,10 @@ module Gapic
99
99
  # returned. Otherwise returns nil.
100
100
  #
101
101
  # @return [Object, Google::Rpc::Status, nil] The result of the operation. If it is an error a
102
- # {Google::Rpc::Status} will be returned.
102
+ # `Google::Rpc::Status` will be returned.
103
103
  def results
104
104
  return error if error?
105
- return response if response?
105
+ response if response?
106
106
  end
107
107
 
108
108
  ##
@@ -119,7 +119,7 @@ module Gapic
119
119
  # Returns the metadata of an operation. If a type is provided, the metadata will be unpacked using the type
120
120
  # provided; returning nil if the metadata is not of the type provided. If the type is not of provided, the
121
121
  # metadata will be unpacked using the metadata's type_url if the type_url is found in the
122
- # {Google::Protobuf::DescriptorPool.generated_pool}. If the type cannot be found the raw metadata is retuned.
122
+ # `Google::Protobuf::DescriptorPool.generated_pool`. If the type cannot be found the raw metadata is retuned.
123
123
  #
124
124
  # @return [Object, nil] The metadata of the operation. Can be nil.
125
125
  #
@@ -250,7 +250,7 @@ module Gapic
250
250
  # @param retry_policy [RetryPolicy, Hash, Proc] The policy for retry. A custom proc that takes the error as an
251
251
  # argument and blocks can also be provided.
252
252
  #
253
- # @yield operation [Gapic::Operation] Yields the finished Operation.
253
+ # @yieldparam operation [Gapic::Operation] Yields the finished Operation.
254
254
  #
255
255
  def wait_until_done! retry_policy: nil
256
256
  retry_policy = RetryPolicy.new(**retry_policy) if retry_policy.is_a? Hash
@@ -270,7 +270,7 @@ module Gapic
270
270
  # Registers a callback to be run when a refreshed operation is marked as done. If the operation has completed
271
271
  # prior to a call to this function the callback will be called instead of registered.
272
272
  #
273
- # @yield operation [Gapic::Operation] Yields the finished Operation.
273
+ # @yieldparam operation [Gapic::Operation] Yields the finished Operation.
274
274
  #
275
275
  def on_done &block
276
276
  if done?
@@ -16,7 +16,8 @@ require "google/protobuf/timestamp_pb"
16
16
 
17
17
  module Gapic
18
18
  ##
19
- # TODO: Describe Protobuf
19
+ # A set of internal utilities for coercing data to protobuf messages.
20
+ #
20
21
  module Protobuf
21
22
  ##
22
23
  # Creates an instance of a protobuf message from a hash that may include nested hashes. `google/protobuf` allows
@@ -30,11 +31,15 @@ module Gapic
30
31
  # @return [Object] An instance of the given message class.
31
32
  def self.coerce hash, to:
32
33
  return hash if hash.is_a? to
34
+ return nil if hash.nil?
35
+
36
+ # Special case handling of certain types
37
+ return time_to_timestamp hash if to == Google::Protobuf::Timestamp && hash.is_a?(Time)
33
38
 
34
39
  # Sanity check: input must be a Hash
35
40
  raise ArgumentError, "Value #{hash} must be a Hash or a #{to.name}" unless hash.is_a? Hash
36
41
 
37
- hash = coerce_submessages hash, to
42
+ hash = coerce_submessages hash, to.descriptor
38
43
  to.new hash
39
44
  end
40
45
 
@@ -44,89 +49,58 @@ module Gapic
44
49
  # @private
45
50
  #
46
51
  # @param hash [Hash] The hash whose nested hashes will be coerced.
47
- # @param message_class [Class] The corresponding protobuf message class of the given hash.
52
+ # @param message_descriptor [Google::Protobuf::Descriptor] The protobuf descriptor for the message.
48
53
  #
49
54
  # @return [Hash] A hash whose nested hashes have been coerced.
50
- def self.coerce_submessages hash, message_class
55
+ def self.coerce_submessages hash, message_descriptor
51
56
  return nil if hash.nil?
52
57
  coerced = {}
53
- message_descriptor = message_class.descriptor
54
58
  hash.each do |key, val|
55
59
  field_descriptor = message_descriptor.lookup key.to_s
56
- coerced[key] = if field_descriptor && field_descriptor.type == :message
57
- coerce_submessage val, field_descriptor
58
- elsif field_descriptor && field_descriptor.type == :bytes &&
59
- (val.is_a?(IO) || val.is_a?(StringIO))
60
- val.binmode.read
61
- else
62
- # `google/protobuf` should throw an error if no field descriptor is
63
- # found. Simply pass through.
64
- val
65
- end
60
+ coerced[key] =
61
+ if field_descriptor&.type == :message
62
+ coerce_submessage val, field_descriptor
63
+ elsif field_descriptor&.type == :bytes && (val.is_a?(IO) || val.is_a?(StringIO))
64
+ val.binmode.read
65
+ else
66
+ # For non-message fields, just pass the scalar value through.
67
+ # Note: if field_descriptor is not found, we just pass the value
68
+ # through and let protobuf raise an error.
69
+ val
70
+ end
66
71
  end
67
72
  coerced
68
73
  end
69
74
 
70
75
  ##
71
- # Coerces the value of a field to be acceptable by the instantiation method of the wrapping message.
76
+ # Coerces a message-typed field.
77
+ # The field can be a normal single message, a repeated message, or a map.
72
78
  #
73
79
  # @private
74
80
  #
75
- # @param val [Object] The value to be coerced.
76
- # @param field_descriptor [Google::Protobuf::FieldDescriptor] The field descriptor of the value.
81
+ # @param val [Object] The value to coerce
82
+ # @param field_descriptor [Google::Protobuf::FieldDescriptor] The field descriptor.
77
83
  #
78
- # @return [Object] The coerced version of the given value.
79
84
  def self.coerce_submessage val, field_descriptor
80
- if (field_descriptor.label == :repeated) && !(map_field? field_descriptor)
81
- coerce_array val, field_descriptor
82
- elsif field_descriptor.subtype.msgclass == Google::Protobuf::Timestamp && val.is_a?(Time)
83
- time_to_timestamp val
85
+ if val.is_a? Array
86
+ # Assume this is a repeated message field, iterate over it and coerce
87
+ # each to the message class.
88
+ # Protobuf will raise an error if this assumption is incorrect.
89
+ val.map do |elem|
90
+ coerce elem, to: field_descriptor.subtype.msgclass
91
+ end
92
+ elsif field_descriptor.label == :repeated
93
+ # Non-array passed to a repeated field: assume this is a map, and that
94
+ # a hash is being passed, and let protobuf handle the conversion.
95
+ # Protobuf will raise an error if this assumption is incorrect.
96
+ val
84
97
  else
85
- coerce_value val, field_descriptor
98
+ # Assume this is a normal single message, and coerce to the message
99
+ # class.
100
+ coerce val, to: field_descriptor.subtype.msgclass
86
101
  end
87
102
  end
88
103
 
89
- ##
90
- # Coerces the values of an array to be acceptable by the instantiation method the wrapping message.
91
- #
92
- # @private
93
- #
94
- # @param array [Array<Object>] The values to be coerced.
95
- # @param field_descriptor [Google::Protobuf::FieldDescriptor] The field descriptor of the values.
96
- #
97
- # @return [Array<Object>] The coerced version of the given values.
98
- def self.coerce_array array, field_descriptor
99
- raise ArgumentError, "Value #{array} must be an array" unless array.is_a? Array
100
- array.map do |val|
101
- coerce_value val, field_descriptor
102
- end
103
- end
104
-
105
- ##
106
- # Hack to determine if field_descriptor is for a map.
107
- #
108
- # TODO(geigerj): Remove this once protobuf Ruby supports an official way
109
- # to determine if a FieldDescriptor represents a map.
110
- # See: https://github.com/google/protobuf/issues/3425
111
- def self.map_field? field_descriptor
112
- (field_descriptor.label == :repeated) &&
113
- (field_descriptor.subtype.name.include? "_MapEntry_")
114
- end
115
-
116
- ##
117
- # Coerces the value of a field to be acceptable by the instantiation method of the wrapping message.
118
- #
119
- # @private
120
- #
121
- # @param val [Object] The value to be coerced.
122
- # @param field_descriptor [Google::Protobuf::FieldDescriptor] The field descriptor of the value.
123
- #
124
- # @return [Object] The coerced version of the given value.
125
- def self.coerce_value val, field_descriptor
126
- return val unless (val.is_a? Hash) && !(map_field? field_descriptor)
127
- coerce val, to: field_descriptor.subtype.msgclass
128
- end
129
-
130
104
  ##
131
105
  # Utility for converting a Google::Protobuf::Timestamp instance to a Ruby time.
132
106
  #
@@ -147,6 +121,6 @@ module Gapic
147
121
  Google::Protobuf::Timestamp.new seconds: time.to_i, nanos: time.nsec
148
122
  end
149
123
 
150
- private_class_method :coerce_submessages, :coerce_submessage, :coerce_array, :coerce_value, :map_field?
124
+ private_class_method :coerce_submessages, :coerce_submessage
151
125
  end
152
126
  end
@@ -117,7 +117,7 @@ module Gapic
117
117
  field_value = extract_scalar_value! request_hash, field_path_camel, field_binding.regex
118
118
 
119
119
  if field_value
120
- field_value = field_value.split("/").map { |segment| percent_escape(segment) }.join("/")
120
+ field_value = field_value.split("/").map { |segment| percent_escape segment }.join("/")
121
121
  end
122
122
 
123
123
  [field_binding.field_path, field_value]
@@ -126,7 +126,7 @@ module Gapic
126
126
 
127
127
  # Percent-escapes a string.
128
128
  # @param str [String] String to escape.
129
- # @return str [String] Escaped string.
129
+ # @return [String] Escaped string.
130
130
  def percent_escape str
131
131
  # `+` to represent spaces is not currently supported in Showcase server.
132
132
  CGI.escape(str).gsub("+", "%20")
@@ -218,7 +218,7 @@ module Gapic
218
218
 
219
219
  # Covers the case where in `foo.bar.baz`, `baz` is still a submessage or an array.
220
220
  return nil if value.is_a?(::Hash) || value.is_a?(::Array)
221
- return value.to_s if value.to_s =~ regex
221
+ value.to_s if value.to_s =~ regex
222
222
  end
223
223
 
224
224
  # Finds a value in the hash by path.
@@ -28,7 +28,7 @@ module Gapic
28
28
 
29
29
  ##
30
30
  # @private
31
- # @param request [::Object, nil, ::Faraday::Response]
31
+ # @param underlying_op [::Object, nil, ::Faraday::Response]
32
32
  # The underlying transport's library object that describes the active call, if any.
33
33
  def initialize underlying_op
34
34
  @underlying_op = underlying_op
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.18.0
4
+ version: 0.20.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: 2023-02-27 00:00:00.000000000 Z
11
+ date: 2023-09-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -132,6 +132,20 @@ dependencies:
132
132
  - - "~>"
133
133
  - !ruby/object:Gem::Version
134
134
  version: '1.36'
135
+ - !ruby/object:Gem::Dependency
136
+ name: concurrent-ruby
137
+ requirement: !ruby/object:Gem::Requirement
138
+ requirements:
139
+ - - "~>"
140
+ - !ruby/object:Gem::Version
141
+ version: 1.2.2
142
+ type: :development
143
+ prerelease: false
144
+ version_requirements: !ruby/object:Gem::Requirement
145
+ requirements:
146
+ - - "~>"
147
+ - !ruby/object:Gem::Version
148
+ version: 1.2.2
135
149
  - !ruby/object:Gem::Dependency
136
150
  name: google-cloud-core
137
151
  requirement: !ruby/object:Gem::Requirement
@@ -300,9 +314,12 @@ files:
300
314
  - lib/gapic/grpc.rb
301
315
  - lib/gapic/grpc/errors.rb
302
316
  - lib/gapic/grpc/service_stub.rb
317
+ - lib/gapic/grpc/service_stub/channel.rb
318
+ - lib/gapic/grpc/service_stub/channel_pool.rb
303
319
  - lib/gapic/grpc/service_stub/rpc_call.rb
304
320
  - lib/gapic/grpc/status_details.rb
305
321
  - lib/gapic/headers.rb
322
+ - lib/gapic/lru_hash.rb
306
323
  - lib/gapic/operation.rb
307
324
  - lib/gapic/operation/retry_policy.rb
308
325
  - lib/gapic/paged_enumerable.rb
@@ -338,7 +355,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
338
355
  - !ruby/object:Gem::Version
339
356
  version: '0'
340
357
  requirements: []
341
- rubygems_version: 3.4.2
358
+ rubygems_version: 3.4.19
342
359
  signing_key:
343
360
  specification_version: 4
344
361
  summary: Common code for GAPIC-generated API clients