google-gax 0.8.6 → 0.9.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 +4 -4
- data/lib/google/gax/api_callable.rb +20 -41
- data/lib/google/gax/errors.rb +5 -0
- data/lib/google/gax/grpc.rb +43 -2
- data/lib/google/gax/util.rb +10 -1
- data/lib/google/gax/version.rb +1 -1
- data/lib/google/gax.rb +0 -1
- data/lib/google/longrunning/operations_client.rb +9 -15
- data/spec/google/gax/grpc_spec.rb +33 -0
- data/spec/google/gax/util_spec.rb +16 -0
- metadata +22 -12
- data/lib/google/gax/credentials.rb +0 -150
- data/spec/google/gax/credentials_spec.rb +0 -108
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: fddb2e602722db6f4c4aa081c4660a6a3291214c
|
|
4
|
+
data.tar.gz: 6be83a2a050318833068ccef972ae886668a5484
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7d6f35c2dcc528491d6a58dd6bf32813708995b20317363cc3eea067ae2358ffdb76da2809a4f12cb1968a1c2361c7cc6235a271c63dcb67e315fbff1a1a9de4
|
|
7
|
+
data.tar.gz: b9e48955378a15a297f967334ed6d7ff629a0a983acfc7e7e61b25bf6e58d87b7340717585aebe70f234973a86b2e86c79fbb1e4fd445f2dea2b010c0a6dfb19
|
|
@@ -32,8 +32,6 @@ require 'time'
|
|
|
32
32
|
require 'google/gax/errors'
|
|
33
33
|
require 'google/gax/bundling'
|
|
34
34
|
|
|
35
|
-
# rubocop:disable Metrics/ModuleLength
|
|
36
|
-
|
|
37
35
|
module Google
|
|
38
36
|
module Gax
|
|
39
37
|
# A class to provide the Enumerable interface for page-streaming method.
|
|
@@ -246,21 +244,9 @@ module Google
|
|
|
246
244
|
add_timeout_arg(func, this_settings.timeout,
|
|
247
245
|
this_settings.kwargs)
|
|
248
246
|
end
|
|
249
|
-
api_call = catch_errors(api_call, settings.errors)
|
|
250
|
-
api_caller.call(api_call, request, this_settings)
|
|
251
|
-
end
|
|
252
|
-
end
|
|
253
|
-
|
|
254
|
-
# Updates a_func to wrap exceptions with GaxError
|
|
255
|
-
#
|
|
256
|
-
# @param a_func [Proc]
|
|
257
|
-
# @param errors [Array<Exception>] Configures the exceptions to wrap.
|
|
258
|
-
# @return [Proc] A proc that will wrap certain exceptions with GaxError.
|
|
259
|
-
def catch_errors(a_func, errors)
|
|
260
|
-
proc do |request|
|
|
261
247
|
begin
|
|
262
|
-
|
|
263
|
-
rescue *errors
|
|
248
|
+
api_caller.call(api_call, request, this_settings)
|
|
249
|
+
rescue *settings.errors
|
|
264
250
|
raise GaxError, 'RPC failed'
|
|
265
251
|
end
|
|
266
252
|
end
|
|
@@ -312,8 +298,6 @@ module Google
|
|
|
312
298
|
enumerable.method(:start)
|
|
313
299
|
end
|
|
314
300
|
|
|
315
|
-
# rubocop:disable Metrics/MethodLength
|
|
316
|
-
|
|
317
301
|
# Creates a proc equivalent to a_func, but that retries on certain
|
|
318
302
|
# exceptions.
|
|
319
303
|
#
|
|
@@ -336,29 +320,24 @@ module Google
|
|
|
336
320
|
delay = retry_options.backoff_settings.initial_retry_delay_millis
|
|
337
321
|
timeout = (retry_options.backoff_settings.initial_rpc_timeout_millis /
|
|
338
322
|
MILLIS_PER_SECOND)
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
delay = [delay * delay_mult, max_delay].min
|
|
355
|
-
timeout = [timeout * timeout_mult, max_timeout, deadline - now].min
|
|
356
|
-
if now >= deadline
|
|
357
|
-
raise RetryError, 'Retry total timeout exceeded with exception'
|
|
358
|
-
end
|
|
323
|
+
deadline = Time.now + total_timeout
|
|
324
|
+
begin
|
|
325
|
+
a_func.call(request, deadline: Time.now + timeout, metadata: kwargs)
|
|
326
|
+
rescue => exception
|
|
327
|
+
unless exception.respond_to?(:code) &&
|
|
328
|
+
retry_options.retry_codes.include?(exception.code)
|
|
329
|
+
raise RetryError, 'Exception occurred in retry method that ' \
|
|
330
|
+
'was not classified as transient'
|
|
331
|
+
end
|
|
332
|
+
sleep(rand(delay) / MILLIS_PER_SECOND)
|
|
333
|
+
now = Time.now
|
|
334
|
+
delay = [delay * delay_mult, max_delay].min
|
|
335
|
+
timeout = [timeout * timeout_mult, max_timeout, deadline - now].min
|
|
336
|
+
if now >= deadline
|
|
337
|
+
raise RetryError, 'Retry total timeout exceeded with exception'
|
|
359
338
|
end
|
|
339
|
+
retry
|
|
360
340
|
end
|
|
361
|
-
result
|
|
362
341
|
end
|
|
363
342
|
end
|
|
364
343
|
|
|
@@ -377,9 +356,9 @@ module Google
|
|
|
377
356
|
end
|
|
378
357
|
end
|
|
379
358
|
|
|
380
|
-
module_function :create_api_call, :
|
|
359
|
+
module_function :create_api_call, :bundleable,
|
|
381
360
|
:page_streamable, :retryable, :add_timeout_arg
|
|
382
|
-
private_class_method :
|
|
361
|
+
private_class_method :bundleable, :page_streamable,
|
|
383
362
|
:retryable, :add_timeout_arg
|
|
384
363
|
end
|
|
385
364
|
end
|
data/lib/google/gax/errors.rb
CHANGED
|
@@ -29,16 +29,21 @@
|
|
|
29
29
|
|
|
30
30
|
require 'English'
|
|
31
31
|
|
|
32
|
+
require 'google/gax/grpc'
|
|
33
|
+
|
|
32
34
|
module Google
|
|
33
35
|
module Gax
|
|
34
36
|
# Common base class for exceptions raised by GAX.
|
|
35
37
|
class GaxError < StandardError
|
|
38
|
+
attr_reader :details
|
|
39
|
+
|
|
36
40
|
# @param msg [String] describes the error that occurred.
|
|
37
41
|
def initialize(msg)
|
|
38
42
|
msg = "GaxError #{msg}"
|
|
39
43
|
msg += ", caused by #{$ERROR_INFO}" if $ERROR_INFO
|
|
40
44
|
super(msg)
|
|
41
45
|
@cause = $ERROR_INFO
|
|
46
|
+
@details = Google::Gax::Grpc.deserialize_error_status_details(@cause)
|
|
42
47
|
end
|
|
43
48
|
|
|
44
49
|
# cause is a new method introduced in 2.1.0, bring this
|
data/lib/google/gax/grpc.rb
CHANGED
|
@@ -28,8 +28,12 @@
|
|
|
28
28
|
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
29
29
|
|
|
30
30
|
require 'grpc'
|
|
31
|
+
require 'grpc/google_rpc_status_utils'
|
|
31
32
|
require 'googleauth'
|
|
32
33
|
require 'google/gax/errors'
|
|
34
|
+
require 'google/protobuf/well_known_types'
|
|
35
|
+
# Required in order to deserialize common error detail proto types
|
|
36
|
+
require 'google/rpc/error_details_pb'
|
|
33
37
|
|
|
34
38
|
module Google
|
|
35
39
|
module Gax
|
|
@@ -43,6 +47,35 @@ module Google
|
|
|
43
47
|
|
|
44
48
|
API_ERRORS = [GRPC::BadStatus, GRPC::Cancelled].freeze
|
|
45
49
|
|
|
50
|
+
def deserialize_error_status_details(error)
|
|
51
|
+
return unless error.is_a? GRPC::BadStatus
|
|
52
|
+
# If error status is malformed, swallow the gRPC error that gets raised.
|
|
53
|
+
begin
|
|
54
|
+
details =
|
|
55
|
+
GRPC::GoogleRpcStatusUtils.extract_google_rpc_status(
|
|
56
|
+
error.to_status
|
|
57
|
+
).details
|
|
58
|
+
rescue
|
|
59
|
+
return 'Could not parse error details due to a malformed server '\
|
|
60
|
+
'response trailer.'
|
|
61
|
+
end
|
|
62
|
+
return if details.nil?
|
|
63
|
+
details =
|
|
64
|
+
GRPC::GoogleRpcStatusUtils.extract_google_rpc_status(
|
|
65
|
+
error.to_status
|
|
66
|
+
).details
|
|
67
|
+
details.map do |any|
|
|
68
|
+
# If the type of the proto wrapped by the Any instance is not
|
|
69
|
+
# available, do not deserialize.
|
|
70
|
+
candidate_class_name = class_case(any.type_name.split('.')).join('::')
|
|
71
|
+
begin
|
|
72
|
+
any.unpack(Object.const_get(candidate_class_name))
|
|
73
|
+
rescue NameError
|
|
74
|
+
any
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
46
79
|
# rubocop:disable Metrics/ParameterLists
|
|
47
80
|
|
|
48
81
|
# Creates a gRPC client stub.
|
|
@@ -97,7 +130,7 @@ module Google
|
|
|
97
130
|
end
|
|
98
131
|
end
|
|
99
132
|
|
|
100
|
-
module_function :create_stub
|
|
133
|
+
module_function :create_stub, :deserialize_error_status_details
|
|
101
134
|
|
|
102
135
|
def self.verify_params(channel, chan_creds, updater_proc)
|
|
103
136
|
if (channel && chan_creds) ||
|
|
@@ -109,7 +142,15 @@ module Google
|
|
|
109
142
|
end
|
|
110
143
|
end
|
|
111
144
|
|
|
112
|
-
|
|
145
|
+
# Capitalize all modules except the message class, which is already
|
|
146
|
+
# correctly cased
|
|
147
|
+
def self.class_case(modules)
|
|
148
|
+
message = modules.pop
|
|
149
|
+
modules = modules.map(&:capitalize)
|
|
150
|
+
modules << message
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
private_class_method :verify_params, :class_case
|
|
113
154
|
end
|
|
114
155
|
end
|
|
115
156
|
end
|
data/lib/google/gax/util.rb
CHANGED
|
@@ -42,12 +42,21 @@ module Google
|
|
|
42
42
|
# messages using hashes but does not allow for nested hashes to instantiate
|
|
43
43
|
# nested submessages.
|
|
44
44
|
#
|
|
45
|
-
# @param hash [Hash] The hash to be converted into a proto message.
|
|
45
|
+
# @param hash [Hash || Class] The hash to be converted into a proto message.
|
|
46
|
+
# If an instance of the proto message class is given, it is returned
|
|
47
|
+
# unchanged.
|
|
46
48
|
# @param message_class [Class] The corresponding protobuf message class of
|
|
47
49
|
# the given hash.
|
|
48
50
|
#
|
|
49
51
|
# @return [Object] An instance of the given message class.
|
|
50
52
|
def to_proto(hash, message_class)
|
|
53
|
+
return hash if hash.is_a? message_class
|
|
54
|
+
|
|
55
|
+
# Sanity check: input must be a Hash
|
|
56
|
+
unless hash.is_a? Hash
|
|
57
|
+
raise ArgumentError,
|
|
58
|
+
"Value #{hash} must be a Hash or a #{message_class.name}"
|
|
59
|
+
end
|
|
51
60
|
hash = coerce_submessages(hash, message_class)
|
|
52
61
|
message_class.new(hash)
|
|
53
62
|
end
|
data/lib/google/gax/version.rb
CHANGED
data/lib/google/gax.rb
CHANGED
|
@@ -40,6 +40,7 @@ require "json"
|
|
|
40
40
|
require "pathname"
|
|
41
41
|
|
|
42
42
|
require "google/gax"
|
|
43
|
+
require "googleauth"
|
|
43
44
|
|
|
44
45
|
require "google/longrunning/operations_pb"
|
|
45
46
|
|
|
@@ -82,15 +83,10 @@ module Google
|
|
|
82
83
|
ALL_SCOPES = [
|
|
83
84
|
].freeze
|
|
84
85
|
|
|
85
|
-
# @param
|
|
86
|
-
# The domain name of the API remote host.
|
|
87
|
-
# @param port [Integer]
|
|
88
|
-
# The port on which to connect to the remote host.
|
|
89
|
-
# @param credentials
|
|
90
|
-
# [Google::Gax::Credentials, String, Hash, GRPC::Core::Channel, GRPC::Core::ChannelCredentials, Proc]
|
|
86
|
+
# @param credentials [Google::Auth::Credentials, String, Hash, GRPC::Core::Channel, GRPC::Core::ChannelCredentials, Proc]
|
|
91
87
|
# Provides the means for authenticating requests made by the client. This parameter can
|
|
92
88
|
# be many types.
|
|
93
|
-
# A `Google::
|
|
89
|
+
# A `Google::Auth::Credentials` uses a the properties of its represented keyfile for
|
|
94
90
|
# authenticating requests made by this client.
|
|
95
91
|
# A `String` will be treated as the path to the keyfile to be used for the construction of
|
|
96
92
|
# credentials for this client.
|
|
@@ -104,7 +100,7 @@ module Google
|
|
|
104
100
|
# @param scopes [Array<String>]
|
|
105
101
|
# The OAuth scopes for this service. This parameter is ignored if
|
|
106
102
|
# an updater_proc is supplied.
|
|
107
|
-
# @param client_config[Hash]
|
|
103
|
+
# @param client_config [Hash]
|
|
108
104
|
# A Hash for call options for each method. See
|
|
109
105
|
# Google::Gax#construct_settings for the structure of
|
|
110
106
|
# this data. Falls back to the default config if not specified
|
|
@@ -121,8 +117,6 @@ module Google
|
|
|
121
117
|
scopes: ALL_SCOPES,
|
|
122
118
|
client_config: {},
|
|
123
119
|
timeout: DEFAULT_TIMEOUT,
|
|
124
|
-
app_name: nil,
|
|
125
|
-
app_version: nil,
|
|
126
120
|
lib_name: nil,
|
|
127
121
|
lib_version: ""
|
|
128
122
|
# These require statements are intentionally placed here to initialize
|
|
@@ -138,14 +132,14 @@ module Google
|
|
|
138
132
|
credentials ||= chan_creds
|
|
139
133
|
credentials ||= updater_proc
|
|
140
134
|
end
|
|
141
|
-
if
|
|
142
|
-
warn "`
|
|
135
|
+
if service_path != SERVICE_ADDRESS || port != DEFAULT_SERVICE_PORT
|
|
136
|
+
warn "`service_path` and `port` parameters are deprecated and will be removed"
|
|
143
137
|
end
|
|
144
138
|
|
|
145
|
-
credentials ||= Google::
|
|
139
|
+
credentials ||= Google::Auth::Credentials.default(scopes: scopes)
|
|
146
140
|
|
|
147
141
|
if credentials.is_a?(String) || credentials.is_a?(Hash)
|
|
148
|
-
updater_proc = Google::
|
|
142
|
+
updater_proc = Google::Auth::Credentials.new(credentials).updater_proc
|
|
149
143
|
end
|
|
150
144
|
if credentials.is_a?(GRPC::Core::Channel)
|
|
151
145
|
channel = credentials
|
|
@@ -156,7 +150,7 @@ module Google
|
|
|
156
150
|
if credentials.is_a?(Proc)
|
|
157
151
|
updater_proc = credentials
|
|
158
152
|
end
|
|
159
|
-
if credentials.is_a?(Google::
|
|
153
|
+
if credentials.is_a?(Google::Auth::Credentials)
|
|
160
154
|
updater_proc = credentials.updater_proc
|
|
161
155
|
end
|
|
162
156
|
|
|
@@ -114,4 +114,37 @@ describe Google::Gax::Grpc do
|
|
|
114
114
|
end.to raise_error(ArgumentError)
|
|
115
115
|
end
|
|
116
116
|
end
|
|
117
|
+
describe '#deserialize_error_status_details' do
|
|
118
|
+
it 'deserializes a known error type' do
|
|
119
|
+
expected_error = Google::Rpc::DebugInfo.new(detail: 'shoes are untied')
|
|
120
|
+
|
|
121
|
+
any = Google::Protobuf::Any.new
|
|
122
|
+
any.pack(expected_error)
|
|
123
|
+
status = Google::Rpc::Status.new(details: [any])
|
|
124
|
+
encoded = Google::Rpc::Status.encode(status)
|
|
125
|
+
metadata = {
|
|
126
|
+
'grpc-status-details-bin' => encoded
|
|
127
|
+
}
|
|
128
|
+
error = GRPC::BadStatus.new(1, '', metadata)
|
|
129
|
+
|
|
130
|
+
expect(Google::Gax::Grpc.deserialize_error_status_details(error))
|
|
131
|
+
.to eq [expected_error]
|
|
132
|
+
end
|
|
133
|
+
it 'does not deserialize an unknown error type' do
|
|
134
|
+
expected_error = Random.new.bytes(8)
|
|
135
|
+
|
|
136
|
+
any = Google::Protobuf::Any.new(
|
|
137
|
+
type_url: 'unknown-type', value: expected_error
|
|
138
|
+
)
|
|
139
|
+
status = Google::Rpc::Status.new(details: [any])
|
|
140
|
+
encoded = Google::Rpc::Status.encode(status)
|
|
141
|
+
metadata = {
|
|
142
|
+
'grpc-status-details-bin' => encoded
|
|
143
|
+
}
|
|
144
|
+
error = GRPC::BadStatus.new(1, '', metadata)
|
|
145
|
+
|
|
146
|
+
expect(Google::Gax::Grpc.deserialize_error_status_details(error))
|
|
147
|
+
.to eq [any]
|
|
148
|
+
end
|
|
149
|
+
end
|
|
117
150
|
end
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
29
29
|
|
|
30
30
|
require 'google/gax'
|
|
31
|
+
require 'google/protobuf/any_pb'
|
|
31
32
|
require 'spec/fixtures/fixture_pb'
|
|
32
33
|
|
|
33
34
|
describe Google::Gax do
|
|
@@ -118,5 +119,20 @@ describe Google::Gax do
|
|
|
118
119
|
Google::Gax.to_proto(user_hash, Google::Protobuf::User)
|
|
119
120
|
end.to raise_error(ArgumentError)
|
|
120
121
|
end
|
|
122
|
+
|
|
123
|
+
it 'handles proto messages' do
|
|
124
|
+
user_message = Google::Protobuf::User.new(
|
|
125
|
+
name: USER_NAME, type: USER_TYPE
|
|
126
|
+
)
|
|
127
|
+
user = Google::Gax.to_proto(user_message, Google::Protobuf::User)
|
|
128
|
+
expect(user).to eq user_message
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
it 'fails if proto message has unexpected type' do
|
|
132
|
+
user_message = Google::Protobuf::Any
|
|
133
|
+
expect do
|
|
134
|
+
Google::Gax.to_proto(user_message, Google::Protobuf::User)
|
|
135
|
+
end.to raise_error(ArgumentError)
|
|
136
|
+
end
|
|
121
137
|
end
|
|
122
138
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: google-gax
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.9.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Google API Authors
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2017-
|
|
11
|
+
date: 2017-11-02 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: googleauth
|
|
@@ -16,42 +16,54 @@ dependencies:
|
|
|
16
16
|
requirements:
|
|
17
17
|
- - "~>"
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: 0.
|
|
19
|
+
version: 0.6.1
|
|
20
20
|
type: :runtime
|
|
21
21
|
prerelease: false
|
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
23
|
requirements:
|
|
24
24
|
- - "~>"
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: 0.
|
|
26
|
+
version: 0.6.1
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
28
|
name: grpc
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
30
30
|
requirements:
|
|
31
|
-
- - "
|
|
31
|
+
- - ">="
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: 1.7.2
|
|
34
|
+
- - "<"
|
|
32
35
|
- !ruby/object:Gem::Version
|
|
33
|
-
version: '
|
|
36
|
+
version: '2.0'
|
|
34
37
|
type: :runtime
|
|
35
38
|
prerelease: false
|
|
36
39
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
40
|
requirements:
|
|
38
|
-
- - "
|
|
41
|
+
- - ">="
|
|
42
|
+
- !ruby/object:Gem::Version
|
|
43
|
+
version: 1.7.2
|
|
44
|
+
- - "<"
|
|
39
45
|
- !ruby/object:Gem::Version
|
|
40
|
-
version: '
|
|
46
|
+
version: '2.0'
|
|
41
47
|
- !ruby/object:Gem::Dependency
|
|
42
48
|
name: googleapis-common-protos
|
|
43
49
|
requirement: !ruby/object:Gem::Requirement
|
|
44
50
|
requirements:
|
|
45
|
-
- - "
|
|
51
|
+
- - ">="
|
|
46
52
|
- !ruby/object:Gem::Version
|
|
47
53
|
version: 1.3.5
|
|
54
|
+
- - "<"
|
|
55
|
+
- !ruby/object:Gem::Version
|
|
56
|
+
version: '2.0'
|
|
48
57
|
type: :runtime
|
|
49
58
|
prerelease: false
|
|
50
59
|
version_requirements: !ruby/object:Gem::Requirement
|
|
51
60
|
requirements:
|
|
52
|
-
- - "
|
|
61
|
+
- - ">="
|
|
53
62
|
- !ruby/object:Gem::Version
|
|
54
63
|
version: 1.3.5
|
|
64
|
+
- - "<"
|
|
65
|
+
- !ruby/object:Gem::Version
|
|
66
|
+
version: '2.0'
|
|
55
67
|
- !ruby/object:Gem::Dependency
|
|
56
68
|
name: google-protobuf
|
|
57
69
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -162,7 +174,6 @@ files:
|
|
|
162
174
|
- lib/google/gax/api_callable.rb
|
|
163
175
|
- lib/google/gax/bundling.rb
|
|
164
176
|
- lib/google/gax/constants.rb
|
|
165
|
-
- lib/google/gax/credentials.rb
|
|
166
177
|
- lib/google/gax/errors.rb
|
|
167
178
|
- lib/google/gax/grpc.rb
|
|
168
179
|
- lib/google/gax/operation.rb
|
|
@@ -176,7 +187,6 @@ files:
|
|
|
176
187
|
- spec/fixtures/fixture_pb.rb
|
|
177
188
|
- spec/google/gax/api_callable_spec.rb
|
|
178
189
|
- spec/google/gax/bundling_spec.rb
|
|
179
|
-
- spec/google/gax/credentials_spec.rb
|
|
180
190
|
- spec/google/gax/grpc_spec.rb
|
|
181
191
|
- spec/google/gax/operation_spec.rb
|
|
182
192
|
- spec/google/gax/path_template_spec.rb
|
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
# Copyright 2017, Google Inc.
|
|
2
|
-
# All rights reserved.
|
|
3
|
-
#
|
|
4
|
-
# Redistribution and use in source and binary forms, with or without
|
|
5
|
-
# modification, are permitted provided that the following conditions are
|
|
6
|
-
# met:
|
|
7
|
-
#
|
|
8
|
-
# * Redistributions of source code must retain the above copyright
|
|
9
|
-
# notice, this list of conditions and the following disclaimer.
|
|
10
|
-
# * Redistributions in binary form must reproduce the above
|
|
11
|
-
# copyright notice, this list of conditions and the following disclaimer
|
|
12
|
-
# in the documentation and/or other materials provided with the
|
|
13
|
-
# distribution.
|
|
14
|
-
# * Neither the name of Google Inc. nor the names of its
|
|
15
|
-
# contributors may be used to endorse or promote products derived from
|
|
16
|
-
# this software without specific prior written permission.
|
|
17
|
-
#
|
|
18
|
-
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
19
|
-
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
20
|
-
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
21
|
-
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
22
|
-
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
23
|
-
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
24
|
-
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
25
|
-
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
26
|
-
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
27
|
-
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
28
|
-
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
29
|
-
|
|
30
|
-
require 'forwardable'
|
|
31
|
-
require 'googleauth'
|
|
32
|
-
require 'json'
|
|
33
|
-
require 'signet/oauth_2/client'
|
|
34
|
-
|
|
35
|
-
module Google
|
|
36
|
-
module Gax
|
|
37
|
-
# @private
|
|
38
|
-
# Represents the OAuth 2.0 signing logic.
|
|
39
|
-
# This class is intended to be inherited by API-specific classes
|
|
40
|
-
# which overrides the SCOPE constant.
|
|
41
|
-
class Credentials
|
|
42
|
-
TOKEN_CREDENTIAL_URI = 'https://accounts.google.com/o/oauth2/token'.freeze
|
|
43
|
-
AUDIENCE = 'https://accounts.google.com/o/oauth2/token'.freeze
|
|
44
|
-
SCOPE = [].freeze
|
|
45
|
-
PATH_ENV_VARS = [].freeze
|
|
46
|
-
JSON_ENV_VARS = [].freeze
|
|
47
|
-
DEFAULT_PATHS = [].freeze
|
|
48
|
-
|
|
49
|
-
attr_accessor :client
|
|
50
|
-
|
|
51
|
-
##
|
|
52
|
-
# Delegate client methods to the client object.
|
|
53
|
-
extend Forwardable
|
|
54
|
-
def_delegators :@client,
|
|
55
|
-
:token_credential_uri, :audience,
|
|
56
|
-
:scope, :issuer, :signing_key, :updater_proc
|
|
57
|
-
|
|
58
|
-
def initialize(keyfile, scope: nil)
|
|
59
|
-
verify_keyfile_provided! keyfile
|
|
60
|
-
if keyfile.is_a? Signet::OAuth2::Client
|
|
61
|
-
@client = keyfile
|
|
62
|
-
elsif keyfile.is_a? Hash
|
|
63
|
-
hash = stringify_hash_keys keyfile
|
|
64
|
-
hash['scope'] ||= scope
|
|
65
|
-
@client = init_client hash
|
|
66
|
-
else
|
|
67
|
-
verify_keyfile_exists! keyfile
|
|
68
|
-
json = JSON.parse ::File.read(keyfile)
|
|
69
|
-
json['scope'] ||= scope
|
|
70
|
-
@client = init_client json
|
|
71
|
-
end
|
|
72
|
-
@client.fetch_access_token!
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
##
|
|
76
|
-
# Returns the default credentials.
|
|
77
|
-
#
|
|
78
|
-
def self.default(scope: nil)
|
|
79
|
-
env = ->(v) { ENV[v] }
|
|
80
|
-
json = lambda do |v|
|
|
81
|
-
unless ENV[v].nil?
|
|
82
|
-
begin
|
|
83
|
-
JSON.parse ENV[v]
|
|
84
|
-
rescue
|
|
85
|
-
nil
|
|
86
|
-
end
|
|
87
|
-
end
|
|
88
|
-
end
|
|
89
|
-
path = ->(p) { ::File.file? p }
|
|
90
|
-
|
|
91
|
-
# First try to find keyfile file from environment variables.
|
|
92
|
-
self::PATH_ENV_VARS.map(&env).compact.select(&path)
|
|
93
|
-
.each do |file|
|
|
94
|
-
return new file, scope: scope
|
|
95
|
-
end
|
|
96
|
-
# Second try to find keyfile json from environment variables.
|
|
97
|
-
self::JSON_ENV_VARS.map(&json).compact.each do |hash|
|
|
98
|
-
return new hash, scope: scope
|
|
99
|
-
end
|
|
100
|
-
# Third try to find keyfile file from known file paths.
|
|
101
|
-
self::DEFAULT_PATHS.select(&path).each do |file|
|
|
102
|
-
return new file, scope: scope
|
|
103
|
-
end
|
|
104
|
-
# Finally get instantiated client from Google::Auth.
|
|
105
|
-
scope ||= self::SCOPE
|
|
106
|
-
client = Google::Auth.get_application_default scope
|
|
107
|
-
new client
|
|
108
|
-
end
|
|
109
|
-
|
|
110
|
-
protected
|
|
111
|
-
|
|
112
|
-
# Verify that the keyfile argument is provided.
|
|
113
|
-
def verify_keyfile_provided!(keyfile)
|
|
114
|
-
return unless keyfile.nil?
|
|
115
|
-
raise 'The keyfile passed to Google::Gax::Credentials.new was nil.'
|
|
116
|
-
end
|
|
117
|
-
|
|
118
|
-
# Verify that the keyfile argument is a file.
|
|
119
|
-
def verify_keyfile_exists!(keyfile)
|
|
120
|
-
exists = ::File.file? keyfile
|
|
121
|
-
raise "The keyfile '#{keyfile}' is not a valid file." unless exists
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
# Initializes the Signet client.
|
|
125
|
-
def init_client(keyfile)
|
|
126
|
-
client_opts = client_options keyfile
|
|
127
|
-
Signet::OAuth2::Client.new client_opts
|
|
128
|
-
end
|
|
129
|
-
|
|
130
|
-
# returns a new Hash with string keys instead of symbol keys.
|
|
131
|
-
def stringify_hash_keys(hash)
|
|
132
|
-
Hash[hash.map { |k, v| [k.to_s, v] }]
|
|
133
|
-
end
|
|
134
|
-
|
|
135
|
-
def client_options(options)
|
|
136
|
-
# Keyfile options have higher priority over constructor defaults
|
|
137
|
-
options['token_credential_uri'] ||= self.class::TOKEN_CREDENTIAL_URI
|
|
138
|
-
options['audience'] ||= self.class::AUDIENCE
|
|
139
|
-
options['scope'] ||= self.class::SCOPE
|
|
140
|
-
|
|
141
|
-
# client options for initializing signet client
|
|
142
|
-
{ token_credential_uri: options['token_credential_uri'],
|
|
143
|
-
audience: options['audience'],
|
|
144
|
-
scope: Array(options['scope']),
|
|
145
|
-
issuer: options['client_email'],
|
|
146
|
-
signing_key: OpenSSL::PKey::RSA.new(options['private_key']) }
|
|
147
|
-
end
|
|
148
|
-
end
|
|
149
|
-
end
|
|
150
|
-
end
|
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
# Copyright 2017, Google Inc.
|
|
2
|
-
# All rights reserved.
|
|
3
|
-
#
|
|
4
|
-
# Redistribution and use in source and binary forms, with or without
|
|
5
|
-
# modification, are permitted provided that the following conditions are
|
|
6
|
-
# met:
|
|
7
|
-
#
|
|
8
|
-
# * Redistributions of source code must retain the above copyright
|
|
9
|
-
# notice, this list of conditions and the following disclaimer.
|
|
10
|
-
# * Redistributions in binary form must reproduce the above
|
|
11
|
-
# copyright notice, this list of conditions and the following disclaimer
|
|
12
|
-
# in the documentation and/or other materials provided with the
|
|
13
|
-
# distribution.
|
|
14
|
-
# * Neither the name of Google Inc. nor the names of its
|
|
15
|
-
# contributors may be used to endorse or promote products derived from
|
|
16
|
-
# this software without specific prior written permission.
|
|
17
|
-
#
|
|
18
|
-
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
19
|
-
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
20
|
-
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
21
|
-
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
22
|
-
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
23
|
-
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
24
|
-
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
25
|
-
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
26
|
-
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
27
|
-
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
28
|
-
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
29
|
-
|
|
30
|
-
require 'google/gax/credentials'
|
|
31
|
-
|
|
32
|
-
# This test is testing the private class Google::Gax::Credentials. We want to
|
|
33
|
-
# make sure that the passed in scope propogates to the Signet object. This means
|
|
34
|
-
# testing the private API, which is generally frowned on.
|
|
35
|
-
describe Google::Gax::Credentials, :private do
|
|
36
|
-
let(:default_keyfile_hash) do
|
|
37
|
-
{
|
|
38
|
-
'private_key_id' => 'testabc1234567890xyz',
|
|
39
|
-
'private_key' => "-----BEGIN RSA PRIVATE KEY-----\nMIIBOwIBAAJBAOyi0Hy1l4Ym2m2o71Q0TF4O9E81isZEsX0bb+Bqz1SXEaSxLiXM\nUZE8wu0eEXivXuZg6QVCW/5l+f2+9UPrdNUCAwEAAQJAJkqubA/Chj3RSL92guy3\nktzeodarLyw8gF8pOmpuRGSiEo/OLTeRUMKKD1/kX4f9sxf3qDhB4e7dulXR1co/\nIQIhAPx8kMW4XTTL6lJYd2K5GrH8uBMp8qL5ya3/XHrBgw3dAiEA7+3Iw3ULTn2I\n1J34WlJ2D5fbzMzB4FAHUNEV7Ys3f1kCIQDtUahCMChrl7+H5t9QS+xrn77lRGhs\nB50pjvy95WXpgQIhAI2joW6JzTfz8fAapb+kiJ/h9Vcs1ZN3iyoRlNFb61JZAiA8\nNy5NyNrMVwtB/lfJf1dAK/p/Bwd8LZLtgM6PapRfgw==\n-----END RSA PRIVATE KEY-----\n",
|
|
40
|
-
'client_email' => 'credz-testabc1234567890xyz@developer.gserviceaccount.com',
|
|
41
|
-
'client_id' => 'credz-testabc1234567890xyz.apps.googleusercontent.com',
|
|
42
|
-
'type' => 'service_account'
|
|
43
|
-
}
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
it 'uses a default scope' do
|
|
47
|
-
mocked_signet = double('Signet::OAuth2::Client')
|
|
48
|
-
allow(mocked_signet).to receive(:fetch_access_token!).and_return(true)
|
|
49
|
-
allow(Signet::OAuth2::Client).to receive(:new) do |options|
|
|
50
|
-
expect(options[:token_credential_uri]).to eq('https://accounts.google.com/o/oauth2/token')
|
|
51
|
-
expect(options[:audience]).to eq('https://accounts.google.com/o/oauth2/token')
|
|
52
|
-
expect(options[:scope]).to eq([])
|
|
53
|
-
expect(options[:issuer]).to eq(default_keyfile_hash['client_email'])
|
|
54
|
-
expect(options[:signing_key]).to be_a_kind_of(OpenSSL::PKey::RSA)
|
|
55
|
-
|
|
56
|
-
mocked_signet
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
Google::Gax::Credentials.new default_keyfile_hash
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
it 'uses a custom scope' do
|
|
63
|
-
mocked_signet = double('Signet::OAuth2::Client')
|
|
64
|
-
allow(mocked_signet).to receive(:fetch_access_token!).and_return(true)
|
|
65
|
-
allow(Signet::OAuth2::Client).to receive(:new) do |options|
|
|
66
|
-
expect(options[:token_credential_uri]).to eq('https://accounts.google.com/o/oauth2/token')
|
|
67
|
-
expect(options[:audience]).to eq('https://accounts.google.com/o/oauth2/token')
|
|
68
|
-
expect(options[:scope]).to eq(['http://example.com/scope'])
|
|
69
|
-
expect(options[:issuer]).to eq(default_keyfile_hash['client_email'])
|
|
70
|
-
expect(options[:signing_key]).to be_a_kind_of(OpenSSL::PKey::RSA)
|
|
71
|
-
|
|
72
|
-
mocked_signet
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
Google::Gax::Credentials.new default_keyfile_hash, scope: 'http://example.com/scope'
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
it 'can be subclassed to pass in other env paths' do
|
|
79
|
-
TEST_PATH_ENV_VAR = 'TEST_PATH'.freeze
|
|
80
|
-
TEST_PATH_ENV_VAL = '/unknown/path/to/file.txt'.freeze
|
|
81
|
-
TEST_JSON_ENV_VAR = 'TEST_JSON_VARS'.freeze
|
|
82
|
-
|
|
83
|
-
ENV[TEST_PATH_ENV_VAR] = TEST_PATH_ENV_VAL
|
|
84
|
-
ENV[TEST_JSON_ENV_VAR] = JSON.generate(default_keyfile_hash)
|
|
85
|
-
|
|
86
|
-
class TestCredentials < Google::Gax::Credentials
|
|
87
|
-
SCOPE = 'http://example.com/scope'.freeze
|
|
88
|
-
PATH_ENV_VARS = [TEST_PATH_ENV_VAR].freeze
|
|
89
|
-
JSON_ENV_VARS = [TEST_JSON_ENV_VAR].freeze
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
allow(::File).to receive(:file?).with(TEST_PATH_ENV_VAL) { false }
|
|
93
|
-
|
|
94
|
-
mocked_signet = double('Signet::OAuth2::Client')
|
|
95
|
-
allow(mocked_signet).to receive(:fetch_access_token!).and_return(true)
|
|
96
|
-
allow(Signet::OAuth2::Client).to receive(:new) do |options|
|
|
97
|
-
expect(options[:token_credential_uri]).to eq('https://accounts.google.com/o/oauth2/token')
|
|
98
|
-
expect(options[:audience]).to eq('https://accounts.google.com/o/oauth2/token')
|
|
99
|
-
expect(options[:scope]).to eq(['http://example.com/scope'])
|
|
100
|
-
expect(options[:issuer]).to eq(default_keyfile_hash['client_email'])
|
|
101
|
-
expect(options[:signing_key]).to be_a_kind_of(OpenSSL::PKey::RSA)
|
|
102
|
-
|
|
103
|
-
mocked_signet
|
|
104
|
-
end
|
|
105
|
-
|
|
106
|
-
TestCredentials.default
|
|
107
|
-
end
|
|
108
|
-
end
|