gapic-common 0.11.1 → 0.13.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: 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: