google-gax 0.1.1
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 +7 -0
- data/Rakefile +15 -0
- data/lib/google/gax.rb +283 -0
- data/lib/google/gax/api_callable.rb +356 -0
- data/lib/google/gax/errors.rb +51 -0
- data/lib/google/gax/grpc.rb +94 -0
- data/lib/google/gax/path_template.rb +248 -0
- data/lib/google/gax/settings.rb +213 -0
- data/lib/google/gax/version.rb +34 -0
- data/spec/google/gax/api_callable_spec.rb +232 -0
- data/spec/google/gax/path_template_spec.rb +159 -0
- data/spec/google/gax/settings_spec.rb +134 -0
- data/spec/spec_helper.rb +8 -0
- metadata +182 -0
@@ -0,0 +1,34 @@
|
|
1
|
+
# Copyright 2016, 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
|
+
module Google
|
31
|
+
module Gax
|
32
|
+
VERSION = '0.1.1'.freeze
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,232 @@
|
|
1
|
+
# Copyright 2016, 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/api_callable'
|
31
|
+
require 'google/gax'
|
32
|
+
|
33
|
+
class CustomException < StandardError
|
34
|
+
attr_reader :code
|
35
|
+
|
36
|
+
def initialize(msg, code)
|
37
|
+
super(msg)
|
38
|
+
@code = code
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
FAKE_STATUS_CODE_1 = :FAKE_STATUS_CODE_1
|
43
|
+
FAKE_STATUS_CODE_2 = :FAKE_STATUS_CODE_2
|
44
|
+
|
45
|
+
describe Google::Gax do
|
46
|
+
CallSettings = Google::Gax::CallSettings
|
47
|
+
|
48
|
+
describe 'create_api_call' do
|
49
|
+
it 'calls api call' do
|
50
|
+
settings = CallSettings.new
|
51
|
+
timeout_arg = nil
|
52
|
+
func = proc do |timeout: nil|
|
53
|
+
timeout_arg = timeout
|
54
|
+
42
|
55
|
+
end
|
56
|
+
my_callable = Google::Gax.create_api_call(func, settings)
|
57
|
+
expect(my_callable.call(nil)).to eq(42)
|
58
|
+
expect(timeout_arg).to_not be_nil
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe 'page streaming' do
|
63
|
+
page_size = 3
|
64
|
+
pages_to_stream = 5
|
65
|
+
|
66
|
+
page_descriptor = Google::Gax::PageDescriptor.new(
|
67
|
+
'page_token', 'next_page_token', 'nums')
|
68
|
+
settings = CallSettings.new(page_descriptor: page_descriptor)
|
69
|
+
timeout_arg = nil
|
70
|
+
func = proc do |request, timeout: nil|
|
71
|
+
timeout_arg = timeout
|
72
|
+
page_token = request['page_token']
|
73
|
+
if page_token > 0 && page_token < page_size * pages_to_stream
|
74
|
+
{ 'nums' => (page_token...(page_token + page_size)),
|
75
|
+
'next_page_token' => page_token + page_size }
|
76
|
+
elsif page_token >= page_size * pages_to_stream
|
77
|
+
{ 'nums' => [] }
|
78
|
+
else
|
79
|
+
{ 'nums' => 0...page_size, 'next_page_token' => page_size }
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'iterates over elements' do
|
84
|
+
my_callable = Google::Gax.create_api_call(func, settings)
|
85
|
+
expect(my_callable.call('page_token' => 0).to_a).to eq(
|
86
|
+
(0...(page_size * pages_to_stream)).to_a)
|
87
|
+
expect(timeout_arg).to_not be_nil
|
88
|
+
end
|
89
|
+
|
90
|
+
it 'offers interface for pages' do
|
91
|
+
my_callable = Google::Gax.create_api_call(func, settings)
|
92
|
+
stream = my_callable.call('page_token' => 0)
|
93
|
+
page = stream.page
|
94
|
+
expect(page.to_a).to eq((0...page_size).to_a)
|
95
|
+
expect(page.next_page_token?).to be_truthy
|
96
|
+
page = stream.next_page
|
97
|
+
expect(page.to_a).to eq((page_size...(page_size * 2)).to_a)
|
98
|
+
|
99
|
+
stream = my_callable.call('page_token' => 0)
|
100
|
+
expect(stream.enum_for(:each_page).to_a.size).to eq(pages_to_stream + 1)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
describe 'retryable' do
|
105
|
+
RetryOptions = Google::Gax::RetryOptions
|
106
|
+
BackoffSettings = Google::Gax::BackoffSettings
|
107
|
+
|
108
|
+
retry_options = RetryOptions.new(
|
109
|
+
[FAKE_STATUS_CODE_1], BackoffSettings.new(0, 0, 0, 0, 0, 0, 1))
|
110
|
+
settings = CallSettings.new(timeout: 0, retry_options: retry_options)
|
111
|
+
|
112
|
+
it 'retries the API call' do
|
113
|
+
time_now = Time.now
|
114
|
+
allow(Time).to receive(:now).and_return(time_now)
|
115
|
+
|
116
|
+
to_attempt = 3
|
117
|
+
|
118
|
+
timeout_arg = nil
|
119
|
+
func = proc do |timeout: nil|
|
120
|
+
timeout_arg = timeout
|
121
|
+
to_attempt -= 1
|
122
|
+
raise CustomException.new('', FAKE_STATUS_CODE_1) if to_attempt > 0
|
123
|
+
1729
|
124
|
+
end
|
125
|
+
my_callable = Google::Gax.create_api_call(func, settings)
|
126
|
+
expect(my_callable.call).to eq(1729)
|
127
|
+
expect(to_attempt).to eq(0)
|
128
|
+
expect(timeout_arg).to_not be_nil
|
129
|
+
end
|
130
|
+
|
131
|
+
it 'doesn\'t retry if no codes' do
|
132
|
+
retry_options = RetryOptions.new([],
|
133
|
+
BackoffSettings.new(1, 2, 3, 4, 5, 6, 7))
|
134
|
+
|
135
|
+
call_count = 0
|
136
|
+
func = proc do
|
137
|
+
call_count += 1
|
138
|
+
raise CustomException.new('', FAKE_STATUS_CODE_1)
|
139
|
+
end
|
140
|
+
my_callable = Google::Gax.create_api_call(
|
141
|
+
func, CallSettings.new(timeout: 0, retry_options: retry_options))
|
142
|
+
expect { my_callable.call }.to raise_error(Google::Gax::RetryError)
|
143
|
+
expect(call_count).to eq(1)
|
144
|
+
end
|
145
|
+
|
146
|
+
it 'aborts retries' do
|
147
|
+
func = proc { raise CustomException.new('', FAKE_STATUS_CODE_1) }
|
148
|
+
my_callable = Google::Gax.create_api_call(func, settings)
|
149
|
+
begin
|
150
|
+
my_callable.call
|
151
|
+
expect(true).to be false # should not reach to this line.
|
152
|
+
rescue Google::Gax::RetryError => exc
|
153
|
+
expect(exc.cause).to be_a(CustomException)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
it 'times out' do
|
158
|
+
to_attempt = 3
|
159
|
+
call_count = 0
|
160
|
+
|
161
|
+
time_now = Time.now
|
162
|
+
allow(Time).to receive(:now).exactly(4).times.and_return(
|
163
|
+
*([time_now] * to_attempt + [time_now + 2]))
|
164
|
+
|
165
|
+
func = proc do
|
166
|
+
call_count += 1
|
167
|
+
raise CustomException.new('', FAKE_STATUS_CODE_1)
|
168
|
+
end
|
169
|
+
|
170
|
+
my_callable = Google::Gax.create_api_call(func, settings)
|
171
|
+
begin
|
172
|
+
my_callable.call
|
173
|
+
except(true).to be false # should not reach to this line.
|
174
|
+
rescue Google::Gax::RetryError => exc
|
175
|
+
expect(exc.cause).to be_a(CustomException)
|
176
|
+
end
|
177
|
+
expect(call_count).to eq(to_attempt)
|
178
|
+
end
|
179
|
+
|
180
|
+
it 'aborts on unexpected exception' do
|
181
|
+
call_count = 0
|
182
|
+
func = proc do
|
183
|
+
call_count += 1
|
184
|
+
raise CustomException.new('', FAKE_STATUS_CODE_2)
|
185
|
+
end
|
186
|
+
my_callable = Google::Gax.create_api_call(func, settings)
|
187
|
+
expect { my_callable.call }.to raise_error(Google::Gax::RetryError)
|
188
|
+
expect(call_count).to eq(1)
|
189
|
+
end
|
190
|
+
|
191
|
+
it 'does not retry even when no responses' do
|
192
|
+
func = proc { nil }
|
193
|
+
my_callable = Google::Gax.create_api_call(func, settings)
|
194
|
+
expect(my_callable.call).to be_nil
|
195
|
+
end
|
196
|
+
|
197
|
+
it 'retries with exponential backoff' do
|
198
|
+
time_now = Time.now
|
199
|
+
start_time = time_now
|
200
|
+
incr_time = proc { |secs| time_now += secs }
|
201
|
+
call_count = 0
|
202
|
+
func = proc do |_, timeout: nil|
|
203
|
+
call_count += 1
|
204
|
+
incr_time.call(timeout)
|
205
|
+
raise CustomException.new(timeout.to_s, FAKE_STATUS_CODE_1)
|
206
|
+
end
|
207
|
+
|
208
|
+
allow(Time).to receive(:now) { time_now }
|
209
|
+
allow(Kernel).to receive(:sleep) { |secs| incr_time.call(secs) }
|
210
|
+
backoff = BackoffSettings.new(3, 2, 24, 5, 2, 80, 2500)
|
211
|
+
retry_options = RetryOptions.new([FAKE_STATUS_CODE_1], backoff)
|
212
|
+
my_callable = Google::Gax.create_api_call(
|
213
|
+
func, CallSettings.new(timeout: 0, retry_options: retry_options))
|
214
|
+
|
215
|
+
begin
|
216
|
+
my_callable.call(0)
|
217
|
+
expect(true).to be false # should not reach to this line.
|
218
|
+
rescue Google::Gax::RetryError => exc
|
219
|
+
expect(exc.cause).to be_a(CustomException)
|
220
|
+
end
|
221
|
+
expect(time_now - start_time).to be >= (
|
222
|
+
backoff.total_timeout_millis / 1000.0)
|
223
|
+
|
224
|
+
calls_lower_bound = backoff.total_timeout_millis / (
|
225
|
+
backoff.max_retry_delay_millis + backoff.max_rpc_timeout_millis)
|
226
|
+
calls_upper_bound = (backoff.total_timeout_millis /
|
227
|
+
backoff.initial_retry_delay_millis)
|
228
|
+
expect(call_count).to be > calls_lower_bound
|
229
|
+
expect(call_count).to be < calls_upper_bound
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
@@ -0,0 +1,159 @@
|
|
1
|
+
# Copyright 2016, 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/path_template'
|
31
|
+
require 'rly'
|
32
|
+
|
33
|
+
describe Google::Gax::PathTemplate do
|
34
|
+
PathTemplate = Google::Gax::PathTemplate
|
35
|
+
|
36
|
+
def symbolize_keys(a_hash)
|
37
|
+
Hash[a_hash.map { |(k, v)| [k.to_sym, v] }]
|
38
|
+
end
|
39
|
+
|
40
|
+
def runtime_error
|
41
|
+
raise_error RuntimeError
|
42
|
+
end
|
43
|
+
|
44
|
+
describe 'method `initialize`' do
|
45
|
+
it 'computes the length correctly' do
|
46
|
+
a_template = PathTemplate.new('a/b/**/*/{a=hello/world}')
|
47
|
+
expect(a_template.size).to eq(6)
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'should fail on invalid tokens' do
|
51
|
+
expect { PathTemplate.new('hello/wor* ld') }.to runtime_error
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'should fail when multiple path wildcards' do
|
55
|
+
expect { PathTemplate.new('buckets/*/**/**/objects/*') }.to runtime_error
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'should fail on inner binding' do
|
59
|
+
expect { PathTemplate.new('buckets/{hello={world}}') }.to runtime_error
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'should fail unexpected eof' do
|
63
|
+
expect { PathTemplate.new('a/{hello=world') }.to runtime_error
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe 'method `match`' do
|
68
|
+
it 'should fail on impossible match' do
|
69
|
+
template = PathTemplate.new('hello/world')
|
70
|
+
expect { template.match('hello') }.to raise_error(ArgumentError)
|
71
|
+
expect { template.match('hello/world/fail') }.to raise_error(
|
72
|
+
ArgumentError)
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'should fail on mismatched literal' do
|
76
|
+
template = PathTemplate.new('hello/world')
|
77
|
+
expect { template.match('hello/world2') }.to raise_error(ArgumentError)
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'should match atomic resource name' do
|
81
|
+
template = PathTemplate.new('buckets/*/*/objects/*')
|
82
|
+
want = { '$0' => 'f', '$1' => 'o', '$2' => 'bar' }
|
83
|
+
expect(template.match('buckets/f/o/objects/bar')).to eq(want)
|
84
|
+
|
85
|
+
template = PathTemplate.new('/buckets/{hello}')
|
86
|
+
want = { 'hello' => 'world' }
|
87
|
+
expect(template.match('buckets/world')).to eq(want)
|
88
|
+
|
89
|
+
template = PathTemplate.new('/buckets/{hello=*}')
|
90
|
+
expect(template.match('buckets/world')).to eq(want)
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'should match escaped chars' do
|
94
|
+
template = PathTemplate.new('buckets/*/objects')
|
95
|
+
want = { '$0' => 'hello%2F%2Bworld' }
|
96
|
+
expect(template.match('buckets/hello%2F%2Bworld/objects')).to eq(want)
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'should match template with unbounded wildcard' do
|
100
|
+
template = PathTemplate.new('buckets/*/objects/**')
|
101
|
+
want = { '$0' => 'foo', '$1' => 'bar/baz' }
|
102
|
+
expect(template.match('buckets/foo/objects/bar/baz')).to eq(want)
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'should match with unbound in the middle' do
|
106
|
+
template = PathTemplate.new('bar/**/foo/*')
|
107
|
+
want = { '$0' => 'foo/foo', '$1' => 'bar' }
|
108
|
+
expect(template.match('bar/foo/foo/foo/bar')).to eq(want)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
describe 'method `render`' do
|
113
|
+
it 'should render atomic resource' do
|
114
|
+
template = PathTemplate.new('buckets/*/*/*/objects/*')
|
115
|
+
params = symbolize_keys(
|
116
|
+
'$0' => 'f',
|
117
|
+
'$1' => 'o',
|
118
|
+
'$2' => 'o',
|
119
|
+
'$3' => 'google.com:a-b'
|
120
|
+
)
|
121
|
+
|
122
|
+
want = 'buckets/f/o/o/objects/google.com:a-b'
|
123
|
+
expect(template.render(params)).to eq(want)
|
124
|
+
end
|
125
|
+
|
126
|
+
it 'should fail when there are too few variables' do
|
127
|
+
template = PathTemplate.new('buckets/*/*/*/objects/*')
|
128
|
+
params = symbolize_keys(
|
129
|
+
'$0' => 'f',
|
130
|
+
'$1' => 'o',
|
131
|
+
'$2' => 'o'
|
132
|
+
)
|
133
|
+
testf = proc { template.render(**params) }
|
134
|
+
expect(testf).to raise_error ArgumentError
|
135
|
+
end
|
136
|
+
|
137
|
+
it 'should succeed with unbound in the middle' do
|
138
|
+
template = PathTemplate.new('bar/**/foo/*')
|
139
|
+
params = symbolize_keys('$0' => '1/2', '$1' => '3')
|
140
|
+
want = 'bar/1/2/foo/3'
|
141
|
+
expect(template.render(**params)).to eq(want)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
describe 'method `to_s`' do
|
146
|
+
tests = {
|
147
|
+
'bar/**/foo/*' => 'bar/{$0=**}/foo/{$1=*}',
|
148
|
+
'buckets/*/objects/*' => 'buckets/{$0=*}/objects/{$1=*}',
|
149
|
+
'/buckets/{hello}' => 'buckets/{hello=*}',
|
150
|
+
'/buckets/{hello=what}/{world}' => 'buckets/{hello=what}/{world=*}',
|
151
|
+
'/buckets/helloazAZ09-.~_what' => 'buckets/helloazAZ09-.~_what'
|
152
|
+
}
|
153
|
+
tests.each do |t, want|
|
154
|
+
it "should render method #{t} ok" do
|
155
|
+
expect(PathTemplate.new(t).to_s).to eq(want)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
# Copyright 2016, 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/settings'
|
31
|
+
require 'google/gax'
|
32
|
+
|
33
|
+
SERVICE_NAME = 'test.interface.v1.api'.freeze
|
34
|
+
|
35
|
+
A_CONFIG = {
|
36
|
+
'interfaces' => {
|
37
|
+
SERVICE_NAME => {
|
38
|
+
'retry_codes' => {
|
39
|
+
'foo_retry' => %w(code_a code_b),
|
40
|
+
'bar_retry' => %w(code_c)
|
41
|
+
},
|
42
|
+
'retry_params' => {
|
43
|
+
'default' => {
|
44
|
+
'initial_retry_delay_millis' => 100,
|
45
|
+
'retry_delay_multiplier' => 1.2,
|
46
|
+
'max_retry_delay_millis' => 1000,
|
47
|
+
'initial_rpc_timeout_millis' => 300,
|
48
|
+
'rpc_timeout_multiplier' => 1.3,
|
49
|
+
'max_rpc_timeout_millis' => 3000,
|
50
|
+
'total_timeout_millis' => 30_000
|
51
|
+
}
|
52
|
+
},
|
53
|
+
'methods' => {
|
54
|
+
# Note that GAX should normalize this to snake case
|
55
|
+
'BundlingMethod' => {
|
56
|
+
'retry_codes_name' => 'foo_retry',
|
57
|
+
'retry_params_name' => 'default',
|
58
|
+
'bundling' => {
|
59
|
+
'element_count_threshold' => 6,
|
60
|
+
'element_count_limit' => 10
|
61
|
+
}
|
62
|
+
},
|
63
|
+
'PageStreamingMethod' => {
|
64
|
+
'retry_codes_name' => 'bar_retry',
|
65
|
+
'retry_params_name' => 'default'
|
66
|
+
}
|
67
|
+
}
|
68
|
+
}
|
69
|
+
}
|
70
|
+
}.freeze
|
71
|
+
|
72
|
+
PAGE_DESCRIPTORS = {
|
73
|
+
'page_streaming_method' => Google::Gax::PageDescriptor.new(
|
74
|
+
'page_token', 'next_page_token', 'page_streams')
|
75
|
+
}.freeze
|
76
|
+
|
77
|
+
BUNDLE_DESCRIPTORS = {
|
78
|
+
'bundling_method' => Google::Gax::BundleDescriptor.new('bundled_field', [])
|
79
|
+
}.freeze
|
80
|
+
|
81
|
+
RETRY_DICT = {
|
82
|
+
'code_a' => 1,
|
83
|
+
'code_b' => 2,
|
84
|
+
'code_c' => 3
|
85
|
+
}.freeze
|
86
|
+
|
87
|
+
describe Google::Gax do
|
88
|
+
it 'creates settings' do
|
89
|
+
defaults = Google::Gax.construct_settings(
|
90
|
+
SERVICE_NAME, A_CONFIG, {}, {}, RETRY_DICT, 30,
|
91
|
+
bundle_descriptors: BUNDLE_DESCRIPTORS,
|
92
|
+
page_descriptors: PAGE_DESCRIPTORS)
|
93
|
+
settings = defaults['bundling_method']
|
94
|
+
expect(settings.timeout).to be(30)
|
95
|
+
# TODO: uncomment this when bundling is added.
|
96
|
+
# expect(settings.bundler).to be_a(Google::Gax::Executor)
|
97
|
+
expect(settings.bundle_descriptor).to be_a(Google::Gax::BundleDescriptor)
|
98
|
+
expect(settings.page_descriptor).to be_nil
|
99
|
+
expect(settings.retry_options).to be_a(Google::Gax::RetryOptions)
|
100
|
+
expect(settings.retry_options.retry_codes).to be_a(Array)
|
101
|
+
expect(settings.retry_options.backoff_settings).to be_a(
|
102
|
+
Google::Gax::BackoffSettings)
|
103
|
+
|
104
|
+
settings = defaults['page_streaming_method']
|
105
|
+
expect(settings.timeout).to be(30)
|
106
|
+
expect(settings.bundler).to be_nil
|
107
|
+
expect(settings.bundle_descriptor).to be_nil
|
108
|
+
expect(settings.page_descriptor).to be_a(Google::Gax::PageDescriptor)
|
109
|
+
expect(settings.retry_options).to be_a(Google::Gax::RetryOptions)
|
110
|
+
expect(settings.retry_options.retry_codes).to be_a(Array)
|
111
|
+
expect(settings.retry_options.backoff_settings).to be_a(
|
112
|
+
Google::Gax::BackoffSettings)
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'overrides settings' do
|
116
|
+
bundling_override = { 'bundling_method' => nil }
|
117
|
+
retry_override = { 'page_streaming_method' => nil }
|
118
|
+
defaults = Google::Gax.construct_settings(
|
119
|
+
SERVICE_NAME, A_CONFIG, bundling_override, retry_override,
|
120
|
+
RETRY_DICT, 30,
|
121
|
+
bundle_descriptors: BUNDLE_DESCRIPTORS,
|
122
|
+
page_descriptors: PAGE_DESCRIPTORS)
|
123
|
+
|
124
|
+
settings = defaults['bundling_method']
|
125
|
+
expect(settings.timeout).to be(30)
|
126
|
+
expect(settings.bundler).to be_nil
|
127
|
+
expect(settings.page_descriptor).to be_nil
|
128
|
+
|
129
|
+
settings = defaults['page_streaming_method']
|
130
|
+
expect(settings.timeout).to be(30)
|
131
|
+
expect(settings.page_descriptor).to be_a(Google::Gax::PageDescriptor)
|
132
|
+
expect(settings.retry_options).to be_nil
|
133
|
+
end
|
134
|
+
end
|