gapic-common 0.11.1 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4362fc972a82bd5c92fdf8172a0ad005f371ecfaf968d251e19bc64b73681652
4
- data.tar.gz: 85ebd85fef5f54aecfec53ffba4a83e2d7b03ecc26c474ffe64f2644241ae74f
3
+ metadata.gz: 5b5964ec91020c76501e2424be598b345a114a16515fe209135e46f552100d4e
4
+ data.tar.gz: 4a08e08fe15b6c6d6a3ad3097895a7d01bcfe3377f1392946a0cfe671ca97fdf
5
5
  SHA512:
6
- metadata.gz: c1b4b4844fbc1380ccb5e82098c9e8647437da5cbfea789d0eb94543c020b10e97187faf5ac600b6b83d8091172b14f29d9cf05bb93017369b8126af21e49415
7
- data.tar.gz: d046faba797ec8b2d76ec66cc2deb12fca86c570a090c05e97ecb844c1aebe8591d56d84a3910a226a8895f0171b1d060ede34a8879c103a76a0ec7da5ad7c7d
6
+ metadata.gz: a0270a8d4933fda1f6297233f3465ae4fed20d34ebbf4bb766f228fe13746b5508c97179eb7c0b763d104c52079f59765b514c32a1d6cb2d61196791f007595a
7
+ data.tar.gz: 437633278a8640eee28a975c7821270b11c2127c72970e0259c29b5b643db37b8d394ac5ae1cd80cb367e10c1384311cc018e0a276804dfda7d8a91f9fffb13a
data/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # Release History
2
2
 
3
+ ### 0.13.0 (2022-10-26)
4
+
5
+ #### Features
6
+
7
+ * Implement server-side streaming support for REST calls in gapic-common ([#826](https://github.com/googleapis/gapic-generator-ruby/issues/826))
8
+
9
+ ### 0.12.0 (2022-09-15)
10
+
11
+ #### Features
12
+
13
+ * Support numeric_enums in the ClientStub ([#817](https://github.com/googleapis/gapic-generator-ruby/issues/817))
14
+ * parse details information from REST errors ([#815](https://github.com/googleapis/gapic-generator-ruby/issues/815))
15
+ * send protobuf version in headers ([#816](https://github.com/googleapis/gapic-generator-ruby/issues/816))
16
+
17
+ #### Bug Fixes
18
+
19
+ * rewrap certain grpc errors ([#810](https://github.com/googleapis/gapic-generator-ruby/issues/810))
20
+ This will rewrap some GRPC::Unavailable errors that were caused by authentication failing as Gapic::GRPC::AuthorizationError which inherits from ::GRPC::Unauthenticated
21
+
3
22
  ### 0.11.1 (2022-08-03)
4
23
 
5
24
  #### Bug Fixes
@@ -132,7 +132,7 @@ module Gapic
132
132
  private
133
133
 
134
134
  def retry? error
135
- error.is_a?(GRPC::BadStatus) && retry_codes.include?(error.code)
135
+ error.is_a?(::GRPC::BadStatus) && retry_codes.include?(error.code)
136
136
  end
137
137
 
138
138
  def delay!
@@ -14,6 +14,6 @@
14
14
 
15
15
  module Gapic
16
16
  module Common
17
- VERSION = "0.11.1".freeze
17
+ VERSION = "0.13.0".freeze
18
18
  end
19
19
  end
@@ -0,0 +1,60 @@
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 "googleauth"
16
+ require "gapic/common/error"
17
+
18
+ module Gapic
19
+ module GRPC
20
+ ##
21
+ # An error class to represent the Authorization Error.
22
+ # The GRPC layer wraps auth plugin errors in ::GRPC::Unavailable.
23
+ # This class rewraps those GRPC layer errors, presenting a correct status code.
24
+ #
25
+ class AuthorizationError < ::GRPC::Unauthenticated
26
+ end
27
+
28
+ ##
29
+ # An error class that represents Deadline Exceeded error with an optional
30
+ # retry root cause.
31
+ #
32
+ # The GRPC layer throws ::GRPC::DeadlineExceeded without any context.
33
+ # If the deadline was exceeded while retrying another exception (e.g.
34
+ # ::GRPC::Unavailable), that exception could be useful for understanding
35
+ # the readon for the timeout.
36
+ #
37
+ # This exception rewraps ::GRPC::DeadlineExceeded, adding an exception
38
+ # that was being retried until the deadline was exceeded (if any) as a
39
+ # `root_cause` attribute.
40
+ #
41
+ # @!attribute [r] root_cause
42
+ # @return [Object, nil] The exception that was being retried
43
+ # when the DeadlineExceeded error occured.
44
+ #
45
+ class DeadlineExceededError < ::GRPC::DeadlineExceeded
46
+ attr_reader :root_cause
47
+
48
+ ##
49
+ # @param message [String] The error message.
50
+ #
51
+ # @param root_cause [Object, nil] The exception that was being retried
52
+ # when the DeadlineExceeded error occured.
53
+ #
54
+ def initialize message, root_cause: nil
55
+ super message
56
+ @root_cause = root_cause
57
+ end
58
+ end
59
+ end
60
+ end
@@ -13,6 +13,7 @@
13
13
  # limitations under the License.
14
14
 
15
15
  require "gapic/call_options"
16
+ require "grpc/errors"
16
17
 
17
18
  module Gapic
18
19
  class ServiceStub
@@ -45,7 +46,7 @@ module Gapic
45
46
  #
46
47
  # @yield [response, operation] Access the response along with the RPC operation.
47
48
  # @yieldparam response [Object] The response object.
48
- # @yieldparam operation [GRPC::ActiveCall::Operation] The RPC operation for the response.
49
+ # @yieldparam operation [::GRPC::ActiveCall::Operation] The RPC operation for the response.
49
50
  #
50
51
  # @return [Object] The response object.
51
52
  #
@@ -55,7 +56,7 @@ module Gapic
55
56
  # require "gapic"
56
57
  # require "gapic/grpc"
57
58
  #
58
- # echo_channel = GRPC::Core::Channel.new(
59
+ # echo_channel = ::GRPC::Core::Channel.new(
59
60
  # "localhost:7469", nil, :this_channel_is_insecure
60
61
  # )
61
62
  # echo_stub = Gapic::ServiceStub.new(
@@ -73,7 +74,7 @@ module Gapic
73
74
  # require "gapic"
74
75
  # require "gapic/grpc"
75
76
  #
76
- # echo_channel = GRPC::Core::Channel.new(
77
+ # echo_channel = ::GRPC::Core::Channel.new(
77
78
  # "localhost:7469", nil, :this_channel_is_insecure
78
79
  # )
79
80
  # echo_stub = Gapic::ServiceStub.new(
@@ -85,7 +86,7 @@ module Gapic
85
86
  # request = Google::Showcase::V1beta1::EchoRequest.new
86
87
  # options = Gapic::CallOptions.new(
87
88
  # retry_policy = {
88
- # retry_codes: [GRPC::Core::StatusCodes::UNAVAILABLE]
89
+ # retry_codes: [::GRPC::Core::StatusCodes::UNAVAILABLE]
89
90
  # }
90
91
  # )
91
92
  # response = echo_call.call request, options: options
@@ -96,7 +97,7 @@ module Gapic
96
97
  # require "gapic"
97
98
  # require "gapic/grpc"
98
99
  #
99
- # echo_channel = GRPC::Core::Channel.new(
100
+ # echo_channel = ::GRPC::Core::Channel.new(
100
101
  # "localhost:7469", nil, :this_channel_is_insecure
101
102
  # )
102
103
  # echo_stub = Gapic::ServiceStub.new(
@@ -116,13 +117,23 @@ module Gapic
116
117
  deadline = calculate_deadline options
117
118
  metadata = options.metadata
118
119
 
120
+ retried_exception = nil
119
121
  begin
120
122
  operation = stub_method.call request, deadline: deadline, metadata: metadata, return_op: true
121
123
  response = operation.execute
122
124
  yield response, operation if block_given?
123
125
  response
126
+ rescue ::GRPC::DeadlineExceeded => e
127
+ raise Gapic::GRPC::DeadlineExceededError.new e.message, root_cause: retried_exception
124
128
  rescue StandardError => e
125
- retry if check_retry?(deadline) && options.retry_policy.call(e)
129
+ if e.is_a?(::GRPC::Unavailable) && /Signet::AuthorizationError/ =~ e.message
130
+ e = Gapic::GRPC::AuthorizationError.new e.message.gsub(%r{^\d+:}, "")
131
+ end
132
+
133
+ if check_retry?(deadline) && options.retry_policy.call(e)
134
+ retried_exception = e
135
+ retry
136
+ end
126
137
 
127
138
  raise e
128
139
  end
@@ -34,20 +34,20 @@ module Gapic
34
34
  # @param grpc_stub_class [Class] gRPC stub class to create a new instance of.
35
35
  # @param endpoint [String] The endpoint of the API.
36
36
  # @param credentials [Google::Auth::Credentials, Signet::OAuth2::Client, String, Hash, Proc,
37
- # GRPC::Core::Channel, GRPC::Core::ChannelCredentials] Provides the means for authenticating requests made by
37
+ # ::GRPC::Core::Channel, ::GRPC::Core::ChannelCredentials] Provides the means for authenticating requests made by
38
38
  # the client. This parameter can be many types:
39
39
  #
40
40
  # * A `Google::Auth::Credentials` uses a the properties of its represented keyfile for authenticating requests
41
41
  # made by this client.
42
42
  # * A `Signet::OAuth2::Client` object used to apply the OAuth credentials.
43
- # * A `GRPC::Core::Channel` will be used to make calls through.
44
- # * A `GRPC::Core::ChannelCredentials` for the setting up the RPC client. The channel credentials should
45
- # already be composed with a `GRPC::Core::CallCredentials` object.
43
+ # * A `::GRPC::Core::Channel` will be used to make calls through.
44
+ # * A `::GRPC::Core::ChannelCredentials` for the setting up the RPC client. The channel credentials should
45
+ # already be composed with a `::GRPC::Core::CallCredentials` object.
46
46
  # * A `Proc` will be used as an updater_proc for the Grpc channel. The proc transforms the metadata for
47
47
  # requests, generally, to give OAuth credentials.
48
48
  # @param channel_args [Hash] The channel arguments. (This argument is ignored when `credentials` is
49
- # provided as a `GRPC::Core::Channel`.)
50
- # @param interceptors [Array<GRPC::ClientInterceptor>] An array of {GRPC::ClientInterceptor} objects that will
49
+ # provided as a `::GRPC::Core::Channel`.)
50
+ # @param interceptors [Array<::GRPC::ClientInterceptor>] An array of {::GRPC::ClientInterceptor} objects that will
51
51
  # be used for intercepting calls before they are executed Interceptors are an EXPERIMENTAL API.
52
52
  #
53
53
  def initialize grpc_stub_class, endpoint:, credentials:, channel_args: nil, interceptors: nil
@@ -59,10 +59,10 @@ module Gapic
59
59
  interceptors = Array interceptors
60
60
 
61
61
  @grpc_stub = case credentials
62
- when GRPC::Core::Channel
62
+ when ::GRPC::Core::Channel
63
63
  grpc_stub_class.new endpoint, nil, channel_override: credentials,
64
64
  interceptors: interceptors
65
- when GRPC::Core::ChannelCredentials, Symbol
65
+ when ::GRPC::Core::ChannelCredentials, Symbol
66
66
  grpc_stub_class.new endpoint, credentials, channel_args: channel_args,
67
67
  interceptors: interceptors
68
68
  else
@@ -70,8 +70,8 @@ module Gapic
70
70
  updater_proc ||= credentials if credentials.is_a? Proc
71
71
  raise ArgumentError, "invalid credentials (#{credentials.class})" if updater_proc.nil?
72
72
 
73
- call_creds = GRPC::Core::CallCredentials.new updater_proc
74
- chan_creds = GRPC::Core::ChannelCredentials.new.compose call_creds
73
+ call_creds = ::GRPC::Core::CallCredentials.new updater_proc
74
+ chan_creds = ::GRPC::Core::ChannelCredentials.new.compose call_creds
75
75
  grpc_stub_class.new endpoint, chan_creds, channel_args: channel_args,
76
76
  interceptors: interceptors
77
77
  end
@@ -88,7 +88,7 @@ module Gapic
88
88
  #
89
89
  # @yield [response, operation] Access the response along with the RPC operation.
90
90
  # @yieldparam response [Object] The response object.
91
- # @yieldparam operation [GRPC::ActiveCall::Operation] The RPC operation for the response.
91
+ # @yieldparam operation [::GRPC::ActiveCall::Operation] The RPC operation for the response.
92
92
  #
93
93
  # @return [Object] The response object.
94
94
  #
@@ -98,7 +98,7 @@ module Gapic
98
98
  # require "gapic"
99
99
  # require "gapic/grpc"
100
100
  #
101
- # echo_channel = GRPC::Core::Channel.new(
101
+ # echo_channel = ::GRPC::Core::Channel.new(
102
102
  # "localhost:7469", nil, :this_channel_is_insecure
103
103
  # )
104
104
  # echo_stub = Gapic::ServiceStub.new(
@@ -115,7 +115,7 @@ module Gapic
115
115
  # require "gapic"
116
116
  # require "gapic/grpc"
117
117
  #
118
- # echo_channel = GRPC::Core::Channel.new(
118
+ # echo_channel = ::GRPC::Core::Channel.new(
119
119
  # "localhost:7469", nil, :this_channel_is_insecure
120
120
  # )
121
121
  # echo_stub = Gapic::ServiceStub.new(
@@ -126,7 +126,7 @@ module Gapic
126
126
  # request = Google::Showcase::V1beta1::EchoRequest.new
127
127
  # options = Gapic::CallOptions.new(
128
128
  # retry_policy = {
129
- # retry_codes: [GRPC::Core::StatusCodes::UNAVAILABLE]
129
+ # retry_codes: [::GRPC::Core::StatusCodes::UNAVAILABLE]
130
130
  # }
131
131
  # )
132
132
  # response = echo_stub.call_rpc :echo, request
@@ -138,7 +138,7 @@ module Gapic
138
138
  # require "gapic"
139
139
  # require "gapic/grpc"
140
140
  #
141
- # echo_channel = GRPC::Core::Channel.new(
141
+ # echo_channel = ::GRPC::Core::Channel.new(
142
142
  # "localhost:7469", nil, :this_channel_is_insecure
143
143
  # )
144
144
  # echo_stub = Gapic::ServiceStub.new(
data/lib/gapic/grpc.rb CHANGED
@@ -13,5 +13,6 @@
13
13
  # limitations under the License.
14
14
 
15
15
  require "grpc"
16
+ require "gapic/grpc/errors"
16
17
  require "gapic/grpc/service_stub"
17
18
  require "gapic/grpc/status_details"
data/lib/gapic/headers.rb CHANGED
@@ -25,7 +25,7 @@ module Gapic
25
25
  # @param lib_version [String] The client library version.
26
26
  # @param gax_version [String] The Gapic version. Defaults to `Gapic::Common::VERSION`.
27
27
  # @param gapic_version [String] The Gapic version.
28
- # @param grpc_version [String] The GRPC version. Defaults to `GRPC::VERSION`.
28
+ # @param grpc_version [String] The GRPC version. Defaults to `::GRPC::VERSION`.
29
29
  # @param rest_version [String] The Rest Library (Faraday) version. Defaults to `Faraday::VERSION`.
30
30
  # @param transports_version_send [Array] Which transports to send versions for.
31
31
  # Allowed values to contain are:
@@ -33,13 +33,14 @@ module Gapic
33
33
  # `:rest` to send the REST library version (if defined)
34
34
  # Defaults to `[:grpc]`
35
35
  def self.x_goog_api_client ruby_version: nil, lib_name: nil, lib_version: nil, gax_version: nil,
36
- gapic_version: nil, grpc_version: nil, rest_version: nil,
36
+ gapic_version: nil, grpc_version: nil, rest_version: nil, protobuf_version: nil,
37
37
  transports_version_send: [:grpc]
38
38
 
39
39
  ruby_version ||= ::RUBY_VERSION
40
40
  gax_version ||= ::Gapic::Common::VERSION
41
41
  grpc_version ||= ::GRPC::VERSION if defined? ::GRPC::VERSION
42
- rest_version ||= ::Faraday::VERSION if defined? ::Faraday
42
+ rest_version ||= ::Faraday::VERSION if defined? ::Faraday::VERSION
43
+ protobuf_version ||= Gem.loaded_specs["google-protobuf"].version.to_s if Gem.loaded_specs.key? "google-protobuf"
43
44
 
44
45
  x_goog_api_client_header = ["gl-ruby/#{ruby_version}"]
45
46
  x_goog_api_client_header << "#{lib_name}/#{lib_version}" if lib_name
@@ -47,6 +48,7 @@ module Gapic
47
48
  x_goog_api_client_header << "gapic/#{gapic_version}" if gapic_version
48
49
  x_goog_api_client_header << "grpc/#{grpc_version}" if grpc_version && transports_version_send.include?(:grpc)
49
50
  x_goog_api_client_header << "rest/#{rest_version}" if rest_version && transports_version_send.include?(:rest)
51
+ x_goog_api_client_header << "pb/#{protobuf_version}" if protobuf_version
50
52
  x_goog_api_client_header.join " ".freeze
51
53
  end
52
54
  end
@@ -53,7 +53,7 @@ module Gapic
53
53
  # @param method_name [Symbol] The RPC method name.
54
54
  # @param request [Object] The request object.
55
55
  # @param response [Object] The response object.
56
- # @param operation [GRPC::ActiveCall::Operation] The RPC operation for the response.
56
+ # @param operation [::GRPC::ActiveCall::Operation] The RPC operation for the response.
57
57
  # @param options [Gapic::CallOptions] The options for making the RPC call.
58
58
  # @param format_resource [Proc] A Proc object to format the resource object. The Proc should accept response as an
59
59
  # argument, and return a formatted resource object. Optional.
@@ -195,7 +195,7 @@ module Gapic
195
195
  # @attribute [r] response
196
196
  # @return [Object] the response object for the page.
197
197
  # @attribute [r] operation
198
- # @return [GRPC::ActiveCall::Operation] the RPC operation for the page.
198
+ # @return [::GRPC::ActiveCall::Operation] the RPC operation for the page.
199
199
  class Page
200
200
  include Enumerable
201
201
  attr_reader :response
@@ -205,7 +205,7 @@ module Gapic
205
205
  # @private
206
206
  # @param response [Object] The response object for the page.
207
207
  # @param resource_field [String] The name of the field in response which holds the resources.
208
- # @param operation [GRPC::ActiveCall::Operation] the RPC operation for the page.
208
+ # @param operation [::GRPC::ActiveCall::Operation] the RPC operation for the page.
209
209
  # @param format_resource [Proc] A Proc object to format the resource object. The Proc should accept response as an
210
210
  # argument, and return a formatted resource object. Optional.
211
211
  #
@@ -32,15 +32,17 @@ module Gapic
32
32
  # @param credentials [Google::Auth::Credentials]
33
33
  # Credentials to send with calls in form of a googleauth credentials object.
34
34
  # (see the [googleauth docs](https://googleapis.dev/ruby/googleauth/latest/index.html))
35
+ # @param numeric_enums [Boolean] Whether to signal the server to JSON-encode enums as ints
35
36
  #
36
37
  # @yield [Faraday::Connection]
37
38
  #
38
- def initialize endpoint:, credentials:
39
+ def initialize endpoint:, credentials:, numeric_enums: false
39
40
  @endpoint = endpoint
40
41
  @endpoint = "https://#{endpoint}" unless /^https?:/.match? endpoint
41
42
  @endpoint = @endpoint.sub %r{/$}, ""
42
43
 
43
44
  @credentials = credentials
45
+ @numeric_enums = numeric_enums
44
46
 
45
47
  @connection = Faraday.new url: @endpoint do |conn|
46
48
  conn.headers = { "Content-Type" => "application/json" }
@@ -58,8 +60,8 @@ module Gapic
58
60
  #
59
61
  # @param uri [String] uri to send this request to
60
62
  # @param params [Hash] query string parameters for the request
61
- # @param options [::Gapic::CallOptions] gapic options to be applied to the REST call.
62
- # Currently only timeout and headers are supported.
63
+ # @param options [::Gapic::CallOptions,Hash] gapic options to be applied
64
+ # to the REST call. Currently only timeout and headers are supported.
63
65
  # @return [Faraday::Response]
64
66
  def make_get_request uri:, params: {}, options: {}
65
67
  make_http_request :get, uri: uri, body: nil, params: params, options: options
@@ -70,8 +72,8 @@ module Gapic
70
72
  #
71
73
  # @param uri [String] uri to send this request to
72
74
  # @param params [Hash] query string parameters for the request
73
- # @param options [::Gapic::CallOptions] gapic options to be applied to the REST call.
74
- # Currently only timeout and headers are supported.
75
+ # @param options [::Gapic::CallOptions,Hash] gapic options to be applied
76
+ # to the REST call. Currently only timeout and headers are supported.
75
77
  # @return [Faraday::Response]
76
78
  def make_delete_request uri:, params: {}, options: {}
77
79
  make_http_request :delete, uri: uri, body: nil, params: params, options: options
@@ -83,8 +85,8 @@ module Gapic
83
85
  # @param uri [String] uri to send this request to
84
86
  # @param body [String] a body to send with the request, nil for requests without a body
85
87
  # @param params [Hash] query string parameters for the request
86
- # @param options [::Gapic::CallOptions] gapic options to be applied to the REST call.
87
- # Currently only timeout and headers are supported.
88
+ # @param options [::Gapic::CallOptions,Hash] gapic options to be applied
89
+ # to the REST call. Currently only timeout and headers are supported.
88
90
  # @return [Faraday::Response]
89
91
  def make_patch_request uri:, body:, params: {}, options: {}
90
92
  make_http_request :patch, uri: uri, body: body, params: params, options: options
@@ -96,8 +98,8 @@ module Gapic
96
98
  # @param uri [String] uri to send this request to
97
99
  # @param body [String] a body to send with the request, nil for requests without a body
98
100
  # @param params [Hash] query string parameters for the request
99
- # @param options [::Gapic::CallOptions] gapic options to be applied to the REST call.
100
- # Currently only timeout and headers are supported.
101
+ # @param options [::Gapic::CallOptions,Hash] gapic options to be applied
102
+ # to the REST call. Currently only timeout and headers are supported.
101
103
  # @return [Faraday::Response]
102
104
  def make_post_request uri:, body: nil, params: {}, options: {}
103
105
  make_http_request :post, uri: uri, body: body, params: params, options: options
@@ -109,8 +111,8 @@ module Gapic
109
111
  # @param uri [String] uri to send this request to
110
112
  # @param body [String] a body to send with the request, nil for requests without a body
111
113
  # @param params [Hash] query string parameters for the request
112
- # @param options [::Gapic::CallOptions] gapic options to be applied to the REST call.
113
- # Currently only timeout and headers are supported.
114
+ # @param options [::Gapic::CallOptions,Hash] gapic options to be applied
115
+ # to the REST call. Currently only timeout and headers are supported.
114
116
  # @return [Faraday::Response]
115
117
  def make_put_request uri:, body: nil, params: {}, options: {}
116
118
  make_http_request :put, uri: uri, body: body, params: params, options: options
@@ -123,15 +125,26 @@ module Gapic
123
125
  # @param uri [String] uri to send this request to
124
126
  # @param body [String, nil] a body to send with the request, nil for requests without a body
125
127
  # @param params [Hash] query string parameters for the request
126
- # @param options [::Gapic::CallOptions] gapic options to be applied to the REST call.
127
- # Currently only timeout and headers are supported.
128
+ # @param options [::Gapic::CallOptions,Hash] gapic options to be applied
129
+ # to the REST call. Currently only timeout and headers are supported.
130
+ # @param is_server_streaming [Boolean] flag if method is streaming
131
+ # @yieldparam chunk [String] The chunk of data received during server streaming.
128
132
  # @return [Faraday::Response]
129
- def make_http_request verb, uri:, body:, params:, options:
133
+ def make_http_request verb, uri:, body:, params:, options:, is_server_streaming: false
134
+ if @numeric_enums && (!params.key?("$alt") || params["$alt"] == "json")
135
+ params = params.merge({ "$alt" => "json;enum-encoding=int" })
136
+ end
137
+ options = ::Gapic::CallOptions.new(**options.to_h) unless options.is_a? ::Gapic::CallOptions
130
138
  @connection.send verb, uri do |req|
131
139
  req.params = params if params.any?
132
140
  req.body = body unless body.nil?
133
141
  req.headers = req.headers.merge options.metadata
134
142
  req.options.timeout = options.timeout if options.timeout&.positive?
143
+ if is_server_streaming
144
+ req.options.on_data = proc do |chunk, _overall_received_bytes|
145
+ yield chunk
146
+ end
147
+ end
135
148
  end
136
149
  end
137
150
  end
@@ -19,16 +19,30 @@ module Gapic
19
19
  module Rest
20
20
  # Gapic REST exception class
21
21
  class Error < ::Gapic::Common::Error
22
- # @return [Integer] the http status code for the error
22
+ # @return [Integer, nil] the http status code for the error
23
23
  attr_reader :status_code
24
+ # @return [Object, nil] the text representation of status as parsed from the response body
25
+ attr_reader :status
26
+ # @return [Object, nil] the details as parsed from the response body
27
+ attr_reader :details
28
+ # @return [Object, nil] the headers of the REST error
29
+ attr_reader :headers
30
+ # The Cloud error wrapper expect to see a `header` property
31
+ alias header headers
24
32
 
25
33
  ##
26
34
  # @param message [String, nil] error message
27
35
  # @param status_code [Integer, nil] HTTP status code of this error
36
+ # @param status [String, nil] The text representation of status as parsed from the response body
37
+ # @param details [Object, nil] Details data of this error
38
+ # @param headers [Object, nil] Http headers data of this error
28
39
  #
29
- def initialize message, status_code
30
- @status_code = status_code
40
+ def initialize message, status_code, status: nil, details: nil, headers: nil
31
41
  super message
42
+ @status_code = status_code
43
+ @status = status
44
+ @details = details
45
+ @headers = headers
32
46
  end
33
47
 
34
48
  class << self
@@ -37,36 +51,85 @@ module Gapic
37
51
  # it tries to parse and set a detailed message and an error code from
38
52
  # from the Google Cloud's response body
39
53
  #
54
+ # @param err [Faraday::Error] the Faraday error to wrap
55
+ #
56
+ # @return [ Gapic::Rest::Error]
40
57
  def wrap_faraday_error err
41
58
  message = err.message
42
59
  status_code = err.response_status
60
+ status = nil
61
+ details = nil
62
+ headers = err.response_headers
43
63
 
44
64
  if err.response_body
45
- msg, code = try_parse_from_body err.response_body
65
+ msg, code, status, details = try_parse_from_body err.response_body
46
66
  message = "An error has occurred when making a REST request: #{msg}" unless msg.nil?
47
67
  status_code = code unless code.nil?
48
68
  end
49
69
 
50
- Gapic::Rest::Error.new message, status_code
70
+ Gapic::Rest::Error.new message, status_code, status: status, details: details, headers: headers
51
71
  end
52
72
 
53
73
  private
54
74
 
55
75
  ##
76
+ # @private
56
77
  # Tries to get the error information from the JSON bodies
57
78
  #
58
79
  # @param body_str [String]
59
- # @return [Array(String, String)]
80
+ # @return [Array(String, String, String, String)]
60
81
  def try_parse_from_body body_str
61
82
  body = JSON.parse body_str
62
- return [nil, nil] unless body && body["error"].is_a?(Hash)
63
83
 
64
- message = body["error"]["message"]
65
- code = body["error"]["code"]
84
+ unless body.is_a?(::Hash) && body&.key?("error") && body["error"].is_a?(::Hash)
85
+ return [nil, nil, nil, nil]
86
+ end
87
+ error = body["error"]
88
+
89
+ message = error["message"] if error.key? "message"
90
+ code = error["code"] if error.key? "code"
91
+ status = error["status"] if error.key? "status"
92
+
93
+ details = parse_details error["details"] if error.key? "details"
66
94
 
67
- [message, code]
95
+ [message, code, status, details]
68
96
  rescue JSON::ParserError
69
- [nil, nil]
97
+ [nil, nil, nil, nil]
98
+ end
99
+
100
+ ##
101
+ # @private
102
+ # Parses the details data, trying to extract the Protobuf.Any objects
103
+ # from it, if it's an array of hashes. Otherwise returns it as is.
104
+ #
105
+ # @param details [Object, nil] the details object
106
+ #
107
+ # @return [Object, nil]
108
+ def parse_details details
109
+ # For rest errors details will contain json representations of `Protobuf.Any`
110
+ # decoded into hashes. If it's not an array, of its elements are not hashes,
111
+ # it's some other case
112
+ return details unless details.is_a? ::Array
113
+
114
+ details.map do |detail_instance|
115
+ next detail_instance unless detail_instance.is_a? ::Hash
116
+ # Next, parse detail_instance into a Proto message.
117
+ # There are three possible issues for the JSON->Any->message parsing
118
+ # - json decoding fails
119
+ # - the json belongs to a proto message type we don't know about
120
+ # - any unpacking fails
121
+ # If we hit any of these three issues we'll just return the original hash
122
+ begin
123
+ any = ::Google::Protobuf::Any.decode_json detail_instance.to_json
124
+ klass = ::Google::Protobuf::DescriptorPool.generated_pool.lookup(any.type_name)&.msgclass
125
+ next detail_instance if klass.nil?
126
+ unpack = any.unpack klass
127
+ next detail_instance if unpack.nil?
128
+ unpack
129
+ rescue ::Google::Protobuf::ParseError
130
+ detail_instance
131
+ end
132
+ end.compact
70
133
  end
71
134
  end
72
135
  end
@@ -0,0 +1,101 @@
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 Rest
19
+ ##
20
+ # A class to provide the Enumerable interface to the response of a REST server-streaming dmethod.
21
+ #
22
+ # ServerStream provides the enumerations over the individual response messages within the stream.
23
+ #
24
+ # @example normal iteration over resources.
25
+ # server_stream.each { |response| puts response }
26
+ #
27
+ class ServerStream
28
+ include Enumerable
29
+
30
+ ##
31
+ # Initializes ServerStream object.
32
+ #
33
+ # @param message_klass [Class]
34
+ # @param json_enumerator [Enumerator<String>]
35
+ def initialize message_klass, json_enumerator
36
+ @json_enumerator = json_enumerator
37
+ @obj = ""
38
+ @message_klass = message_klass
39
+ @ready_objs = [] # List of strings
40
+ end
41
+
42
+ ##
43
+ # Iterate over JSON objects in the streamed response.
44
+ #
45
+ # @yield [Object] Gives one complete Message object.
46
+ #
47
+ # @return [Enumerator] if no block is provided
48
+ #
49
+ def each
50
+ return enum_for :each unless block_given?
51
+
52
+ loop do
53
+ while @ready_objs.length.zero?
54
+ begin
55
+ chunk = @json_enumerator.next
56
+ next unless chunk
57
+ next_json! chunk
58
+ rescue StopIteration
59
+ dangling_content = @obj.strip
60
+ error_expl = "Dangling content left after iterating through the stream. " \
61
+ "This means that not all content was received or parsed correctly. " \
62
+ "It is likely a result of server or network error."
63
+ error_text = "#{error_expl}\n Content left unparsed: #{dangling_content}"
64
+
65
+ raise Gapic::Common::Error, error_text unless dangling_content.empty?
66
+ return
67
+ end
68
+ end
69
+ yield @message_klass.decode_json @ready_objs.shift, ignore_unknown_fields: true
70
+ end
71
+ end
72
+
73
+ private
74
+
75
+ ##
76
+ # Builds the next JSON object of the server stream from chunk.
77
+ #
78
+ # @param chunk [String] Contains (partial) JSON object
79
+ #
80
+ def next_json! chunk
81
+ chunk.chars.each do |char|
82
+ # Invariant: @obj is always either a part of a single JSON object or the entire JSON object.
83
+ # Hence, it's safe to strip whitespace, commans and array brackets. These characters
84
+ # are only added before @obj is a complete JSON object and essentially can be flushed.
85
+ next if @obj.empty? && char != "{"
86
+ @obj += char
87
+ next unless char == "}"
88
+ begin
89
+ # Two choices here: append a Ruby object into
90
+ # ready_objs or a string. Going with the latter here.
91
+ JSON.parse @obj
92
+ @ready_objs.append @obj
93
+ @obj = ""
94
+ rescue JSON::ParserError
95
+ next
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,71 @@
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
+
16
+ module Gapic
17
+ module Rest
18
+ ##
19
+ # @private
20
+ # A class to provide the Enumerable interface to an incoming stream of data.
21
+ #
22
+ # ThreadedEnumerator provides the enumerations over the individual chunks of data received from the server.
23
+ #
24
+ # @example normal iteration over resources.
25
+ # chunk = threaded_enumerator.next
26
+ #
27
+ # @attribute [r] in_q
28
+ # @return [Queue] Input queue.
29
+ # @attribute [r] out_q
30
+ # @return [Queue] Output queue.
31
+ class ThreadedEnumerator
32
+ attr_reader :in_q
33
+ attr_reader :out_q
34
+
35
+ # Spawns a new thread and does appropriate clean-up
36
+ # in case thread fails. Propagates exception back
37
+ # to main thread.
38
+ #
39
+ # @yieldparam in_q[Queue] input queue
40
+ # @yieldparam out_q[Queue] output queue
41
+ def initialize
42
+ @in_q = Queue.new
43
+ @out_q = Queue.new
44
+
45
+ Thread.new do
46
+ yield @in_q, @out_q
47
+ rescue StandardError => e
48
+ @out_q.push e
49
+ end
50
+ end
51
+
52
+ def next
53
+ @in_q.enq :next
54
+ chunk = @out_q.deq
55
+
56
+ if chunk.is_a? StandardError
57
+ @out_q.close
58
+ @in_q.close
59
+ raise chunk
60
+ end
61
+
62
+ if chunk.nil?
63
+ @out_q.close
64
+ @in_q.close
65
+ raise StopIteration
66
+ end
67
+ chunk
68
+ end
69
+ end
70
+ end
71
+ end
data/lib/gapic/rest.rb CHANGED
@@ -27,4 +27,6 @@ require "gapic/rest/faraday_middleware"
27
27
  require "gapic/rest/grpc_transcoder"
28
28
  require "gapic/rest/operation"
29
29
  require "gapic/rest/paged_enumerable"
30
+ require "gapic/rest/server_stream"
31
+ require "gapic/rest/threaded_enumerator"
30
32
  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.11.1
4
+ version: 0.13.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-08-03 00:00:00.000000000 Z
11
+ date: 2022-10-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -297,6 +297,7 @@ files:
297
297
  - lib/gapic/generic_lro/base_operation.rb
298
298
  - lib/gapic/generic_lro/operation.rb
299
299
  - lib/gapic/grpc.rb
300
+ - lib/gapic/grpc/errors.rb
300
301
  - lib/gapic/grpc/service_stub.rb
301
302
  - lib/gapic/grpc/service_stub/rpc_call.rb
302
303
  - lib/gapic/grpc/status_details.rb
@@ -313,6 +314,8 @@ files:
313
314
  - lib/gapic/rest/grpc_transcoder/http_binding.rb
314
315
  - lib/gapic/rest/operation.rb
315
316
  - lib/gapic/rest/paged_enumerable.rb
317
+ - lib/gapic/rest/server_stream.rb
318
+ - lib/gapic/rest/threaded_enumerator.rb
316
319
  - lib/gapic/stream_input.rb
317
320
  homepage: https://github.com/googleapis/gapic-generator-ruby
318
321
  licenses: