google-gax 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- 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,51 @@
|
|
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
|
+
# Common base class for exceptions raised by GAX.
|
33
|
+
class GaxError < StandardError
|
34
|
+
attr_reader :cause
|
35
|
+
|
36
|
+
# @param msg [String] describes the error that occurred.
|
37
|
+
# @param cause [Error] the exception raised by a lower layer of
|
38
|
+
# the RPC stack (for example, gRPC) that caused this
|
39
|
+
# exception, or None if this exception originated in GAX.
|
40
|
+
def initialize(msg, cause:nil)
|
41
|
+
msg = "GaxError #{msg}, caused by #{cause}" if cause
|
42
|
+
super(msg)
|
43
|
+
@cause = cause
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Indicates an error during automatic GAX retrying.
|
48
|
+
class RetryError < GaxError
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,94 @@
|
|
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 'grpc'
|
31
|
+
require 'googleauth'
|
32
|
+
|
33
|
+
module Google
|
34
|
+
module Gax
|
35
|
+
# Grpc adapts the gRPC surface
|
36
|
+
module Grpc
|
37
|
+
STATUS_CODE_NAMES = GRPC::Core::StatusCodes.constants.map do |sym|
|
38
|
+
[sym.to_s, GRPC::Core::StatusCodes.const_get(sym)]
|
39
|
+
end.to_h.freeze
|
40
|
+
|
41
|
+
API_ERRORS = [GRPC::BadStatus, GRPC::Cancelled].freeze
|
42
|
+
|
43
|
+
# rubocop:disable Metrics/ParameterLists
|
44
|
+
|
45
|
+
# Creates a gRPC client stub.
|
46
|
+
#
|
47
|
+
# @param service_path [String] The domain name of the API remote host.
|
48
|
+
#
|
49
|
+
# @param port [Fixnum] The port on which to connect to the remote host.
|
50
|
+
#
|
51
|
+
# @param chan_creds [Object] A ClientCredentials object for use with an
|
52
|
+
# SSL-enabled Channel. If none, credentials are pulled from a default
|
53
|
+
# location.
|
54
|
+
#
|
55
|
+
# @param channel [Object] A Channel object through which to make calls. If
|
56
|
+
# none, a secure channel is constructed.
|
57
|
+
#
|
58
|
+
# @param updater_proc [Proc]
|
59
|
+
# A function that transforms the metadata for requests, e.g., to give
|
60
|
+
# OAuth credentials.
|
61
|
+
#
|
62
|
+
# @param scopes [Array<String>]
|
63
|
+
# The OAuth scopes for this service. This parameter is ignored if
|
64
|
+
# a custom metadata_transformer is supplied.
|
65
|
+
#
|
66
|
+
# @yield [address, creds]
|
67
|
+
# the generated gRPC method to create a stub.
|
68
|
+
#
|
69
|
+
# @return A gRPC client stub.
|
70
|
+
def create_stub(service_path,
|
71
|
+
port,
|
72
|
+
chan_creds: nil,
|
73
|
+
channel: nil,
|
74
|
+
updater_proc: nil,
|
75
|
+
scopes: nil)
|
76
|
+
address = "#{service_path}:#{port}"
|
77
|
+
if channel.nil?
|
78
|
+
chan_creds = GRPC::Core::ChannelCredentials.new if chan_creds.nil?
|
79
|
+
if updater_proc.nil?
|
80
|
+
auth_creds = Google::Auth.get_application_default(scopes)
|
81
|
+
updater_proc = auth_creds.updater_proc
|
82
|
+
end
|
83
|
+
call_creds = GRPC::Core::CallCredentials.new(updater_proc)
|
84
|
+
chan_creds = chan_creds.compose(call_creds)
|
85
|
+
yield(address, chan_creds)
|
86
|
+
else
|
87
|
+
yield(address, nil, channel_override: channel)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
module_function :create_stub
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,248 @@
|
|
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 'rly'
|
31
|
+
|
32
|
+
module Google
|
33
|
+
# Gax defines Google API extensions
|
34
|
+
module Gax
|
35
|
+
# Lexer for the path_template language
|
36
|
+
class PathLex < Rly::Lex
|
37
|
+
token :FORWARD_SLASH, %r{/}
|
38
|
+
token :LEFT_BRACE, /\{/
|
39
|
+
token :RIGHT_BRACE, /\}/
|
40
|
+
token :EQUALS, /=/
|
41
|
+
token :PATH_WILDCARD, /\*\*/ # has to occur before WILDCARD
|
42
|
+
token :WILDCARD, /\*/
|
43
|
+
token :LITERAL, %r{[^*=\}\{\/ ]+}
|
44
|
+
|
45
|
+
on_error do |t|
|
46
|
+
raise t ? "Syntax error at '#{t.value}'" : 'Syntax error at EOF'
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# Parser for the path_template language
|
51
|
+
class PathParse < Rly::Yacc
|
52
|
+
attr_reader :segment_count, :binding_var_count
|
53
|
+
|
54
|
+
def initialize(*args)
|
55
|
+
super
|
56
|
+
@segment_count = 0
|
57
|
+
@binding_var_count = 0
|
58
|
+
end
|
59
|
+
|
60
|
+
def parse(*args)
|
61
|
+
segments = super
|
62
|
+
has_path_wildcard = false
|
63
|
+
raise 'path template has no segments' if segments.nil?
|
64
|
+
segments.each do |s|
|
65
|
+
next unless s.kind == TERMINAL && s.literal == '**'
|
66
|
+
if has_path_wildcard
|
67
|
+
raise 'path template cannot contain more than one path wildcard'
|
68
|
+
else
|
69
|
+
has_path_wildcard = true
|
70
|
+
end
|
71
|
+
end
|
72
|
+
segments
|
73
|
+
end
|
74
|
+
|
75
|
+
rule 'template : FORWARD_SLASH bound_segments
|
76
|
+
| bound_segments' do |template, *segments|
|
77
|
+
template.value = segments[-1].value
|
78
|
+
end
|
79
|
+
|
80
|
+
rule 'bound_segments : bound_segment FORWARD_SLASH bound_segments
|
81
|
+
| bound_segment' do |segs, a_seg, _, more_segs|
|
82
|
+
segs.value = a_seg.value
|
83
|
+
segs.value.push(*more_segs.value) unless more_segs.nil?
|
84
|
+
end
|
85
|
+
|
86
|
+
rule 'unbound_segments : unbound_terminal FORWARD_SLASH unbound_segments
|
87
|
+
| unbound_terminal' do |segs, a_term, _, more_segs|
|
88
|
+
segs.value = a_term.value
|
89
|
+
segs.value.push(*more_segs.value) unless more_segs.nil?
|
90
|
+
end
|
91
|
+
|
92
|
+
rule 'bound_segment : bound_terminal
|
93
|
+
| variable' do |segment, term_or_var|
|
94
|
+
segment.value = term_or_var.value
|
95
|
+
end
|
96
|
+
|
97
|
+
rule 'unbound_terminal : WILDCARD
|
98
|
+
| PATH_WILDCARD
|
99
|
+
| LITERAL' do |term, literal|
|
100
|
+
term.value = [Segment.new(TERMINAL, literal.value)]
|
101
|
+
@segment_count += 1
|
102
|
+
end
|
103
|
+
|
104
|
+
rule 'bound_terminal : unbound_terminal' do |bound, unbound|
|
105
|
+
if ['*', '**'].include?(unbound.value[0].literal)
|
106
|
+
bound.value = [
|
107
|
+
Segment.new(BINDING, format('$%d', @binding_var_count)),
|
108
|
+
unbound.value[0],
|
109
|
+
Segment.new(END_BINDING, '')
|
110
|
+
]
|
111
|
+
@binding_var_count += 1
|
112
|
+
else
|
113
|
+
bound.value = unbound.value
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
rule 'variable : LEFT_BRACE LITERAL EQUALS unbound_segments RIGHT_BRACE
|
118
|
+
| LEFT_BRACE LITERAL RIGHT_BRACE' do |variable, *args|
|
119
|
+
variable.value = [Segment.new(BINDING, args[1].value)]
|
120
|
+
if args.size > 3
|
121
|
+
variable.value.push(*args[3].value)
|
122
|
+
else
|
123
|
+
variable.value.push(Segment.new(TERMINAL, '*'))
|
124
|
+
@segment_count += 1
|
125
|
+
end
|
126
|
+
variable.value.push(Segment.new(END_BINDING, ''))
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
# PathTemplate parses and format resource names
|
131
|
+
class PathTemplate
|
132
|
+
attr_reader :segments, :size
|
133
|
+
|
134
|
+
def initialize(data)
|
135
|
+
parser = PathParse.new(PathLex.new)
|
136
|
+
@segments = parser.parse(data)
|
137
|
+
@size = parser.segment_count
|
138
|
+
end
|
139
|
+
|
140
|
+
# Formats segments as a string.
|
141
|
+
#
|
142
|
+
# @param [Array<Segments>]
|
143
|
+
# The segments to be formatted
|
144
|
+
# @return [String] the formatted output
|
145
|
+
def self.format_segments(*segments)
|
146
|
+
template = ''
|
147
|
+
slash = true
|
148
|
+
segments.each do |segment|
|
149
|
+
if segment.kind == TERMINAL
|
150
|
+
template += '/' if slash
|
151
|
+
template += segment.literal.to_s
|
152
|
+
next
|
153
|
+
end
|
154
|
+
slash = true
|
155
|
+
if segment.kind == BINDING
|
156
|
+
template += "/{#{segment.literal}="
|
157
|
+
slash = false
|
158
|
+
else
|
159
|
+
template += "#{segment.literal}}"
|
160
|
+
end
|
161
|
+
end
|
162
|
+
template[1, template.length] # exclude the initial slash
|
163
|
+
end
|
164
|
+
|
165
|
+
# Renders a path template using the provided bindings.
|
166
|
+
# @param binding [Hash]
|
167
|
+
# A mapping of var names to binding strings.
|
168
|
+
# @return [String] A rendered representation of this path template.
|
169
|
+
# @raise [ArgumentError] If a key isn't provided or if a sub-template
|
170
|
+
# can't be parsed.
|
171
|
+
def render(**bindings)
|
172
|
+
out = []
|
173
|
+
binding = false
|
174
|
+
@segments.each do |segment|
|
175
|
+
if segment.kind == BINDING
|
176
|
+
literal_sym = segment.literal.to_sym
|
177
|
+
unless bindings.key?(literal_sym)
|
178
|
+
msg = "Value for key #{segment.literal} is not provided"
|
179
|
+
raise(ArgumentError, msg)
|
180
|
+
end
|
181
|
+
out.push(*PathTemplate.new(bindings[literal_sym]).segments)
|
182
|
+
binding = true
|
183
|
+
elsif segment.kind == END_BINDING
|
184
|
+
binding = false
|
185
|
+
else
|
186
|
+
next if binding
|
187
|
+
out << segment
|
188
|
+
end
|
189
|
+
end
|
190
|
+
path = self.class.format_segments(*out)
|
191
|
+
match(path)
|
192
|
+
path
|
193
|
+
end
|
194
|
+
|
195
|
+
# Matches a fully qualified path template string.
|
196
|
+
# @param path [String]
|
197
|
+
# A fully qualified path template string.
|
198
|
+
# @return [Hash] Var names to matched binding values.
|
199
|
+
# @raise [ArgumentError] If path can't be matched to the template.
|
200
|
+
def match(path)
|
201
|
+
that = path.split('/')
|
202
|
+
current_var = nil
|
203
|
+
bindings = {}
|
204
|
+
segment_count = @size
|
205
|
+
i = 0
|
206
|
+
@segments.each do |segment|
|
207
|
+
break if i >= that.size
|
208
|
+
if segment.kind == TERMINAL
|
209
|
+
if segment.literal == '*'
|
210
|
+
bindings[current_var] = that[i]
|
211
|
+
i += 1
|
212
|
+
elsif segment.literal == '**'
|
213
|
+
size = that.size - segment_count + 1
|
214
|
+
segment_count += size - 1
|
215
|
+
bindings[current_var] = that[i, size].join('/')
|
216
|
+
i += size
|
217
|
+
elsif segment.literal != that[i]
|
218
|
+
throw ArgumentError.new(
|
219
|
+
"mismatched literal: '#{segment.literal}' != '#{that[i]}'")
|
220
|
+
else
|
221
|
+
i += 1
|
222
|
+
end
|
223
|
+
elsif segment.kind == BINDING
|
224
|
+
current_var = segment.literal
|
225
|
+
end
|
226
|
+
end
|
227
|
+
if i != that.size || i != segment_count
|
228
|
+
throw ArgumentError.new(
|
229
|
+
"match error: could not instantiate a path template from #{path}")
|
230
|
+
end
|
231
|
+
bindings
|
232
|
+
end
|
233
|
+
|
234
|
+
def to_s
|
235
|
+
self.class.format_segments(*@segments)
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
Segment = Struct.new(:kind, :literal)
|
240
|
+
|
241
|
+
# Private constants/methods/classes
|
242
|
+
BINDING = 1
|
243
|
+
END_BINDING = 2
|
244
|
+
TERMINAL = 3
|
245
|
+
|
246
|
+
private_constant :BINDING, :END_BINDING, :TERMINAL
|
247
|
+
end
|
248
|
+
end
|
@@ -0,0 +1,213 @@
|
|
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
|
+
# Helper for #construct_settings
|
33
|
+
#
|
34
|
+
# @param method_config A dictionary representing a single
|
35
|
+
# ``methods`` entry of the standard API client config file. (See
|
36
|
+
# #construct_settings for information on this yaml.)
|
37
|
+
# @param method_retry_override [BundleOptions, :OPTION_INHERIT, nil]
|
38
|
+
# If set to :OPTION_INHERIT, the retry settings are derived from
|
39
|
+
# method config. Otherwise, this parameter overrides
|
40
|
+
# +method_config+.
|
41
|
+
|
42
|
+
# @param bundle_descriptor [BundleDescriptor] A BundleDescriptor
|
43
|
+
# object describing the structure of bundling for this
|
44
|
+
# method. If not set, this method will not bundle.
|
45
|
+
# @return An Executor that configures bundling, or nil if this
|
46
|
+
# method should not bundle.
|
47
|
+
def _construct_bundling(method_config, method_bundling_override,
|
48
|
+
bundle_descriptor)
|
49
|
+
if method_config.key?('bundling') && bundle_descriptor
|
50
|
+
if method_bundling_override == :OPTION_INHERIT
|
51
|
+
options = BundleOptions.new
|
52
|
+
method_config['bundling'].each_pair do |key, value|
|
53
|
+
options[key.intern] = value
|
54
|
+
end
|
55
|
+
# TODO: comment-out when bundling is supported.
|
56
|
+
# Executor.new(options)
|
57
|
+
elsif method_bundling_override
|
58
|
+
# Executor.new(method_bundling_override)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# Helper for #construct_settings
|
64
|
+
#
|
65
|
+
# @param method_config [Hash] A dictionary representing a single
|
66
|
+
# +methods+ entry of the standard API client config file. (See
|
67
|
+
# #construct_settings for information on this yaml.)
|
68
|
+
# @param method_retry_override [RetryOptions, :OPTION_INHERIT, nil]
|
69
|
+
# If set to :OPTION_INHERIT, the retry settings are derived from
|
70
|
+
# method config. Otherwise, this parameter overrides
|
71
|
+
# +method_config+.
|
72
|
+
# @param retry_codes_def [Hash] A dictionary parsed from the
|
73
|
+
# +retry_codes_def+ entry of the standard API client config
|
74
|
+
# file. (See #construct_settings for information on this yaml.)
|
75
|
+
# @param retry_params [Hash] A dictionary parsed from the
|
76
|
+
# +retry_params+ entry of the standard API client config
|
77
|
+
# file. (See #construct_settings for information on this yaml.)
|
78
|
+
# @param retry_names [Hash] A dictionary mapping the string names
|
79
|
+
# used in the standard API client config file to API response
|
80
|
+
# status codes.
|
81
|
+
# @return [RetryOptions, nil]
|
82
|
+
def _construct_retry(method_config, method_retry_override, retry_codes,
|
83
|
+
retry_params, retry_names)
|
84
|
+
unless method_retry_override == :OPTION_INHERIT
|
85
|
+
return method_retry_override
|
86
|
+
end
|
87
|
+
|
88
|
+
retry_codes ||= {}
|
89
|
+
retry_codes_name = method_config['retry_codes_name']
|
90
|
+
codes = retry_codes.fetch(retry_codes_name, []).map do |name|
|
91
|
+
retry_names[name]
|
92
|
+
end
|
93
|
+
|
94
|
+
if retry_params && method_config.key?('retry_params_name')
|
95
|
+
params = retry_params[method_config['retry_params_name']]
|
96
|
+
backoff_settings = BackoffSettings.new(
|
97
|
+
*params.values_at(*BackoffSettings.members.map(&:to_s)))
|
98
|
+
end
|
99
|
+
|
100
|
+
RetryOptions.new(codes, backoff_settings)
|
101
|
+
end
|
102
|
+
|
103
|
+
def _upper_camel_to_lower_underscore(string)
|
104
|
+
string.scan(/[[:upper:]][^[:upper:]]*/).map(&:downcase).join('_')
|
105
|
+
end
|
106
|
+
|
107
|
+
# rubocop:disable Metrics/ParameterLists
|
108
|
+
|
109
|
+
# Constructs a dictionary mapping method names to CallSettings.
|
110
|
+
#
|
111
|
+
# The +client_config+ parameter is parsed from a client configuration JSON
|
112
|
+
# file of the form:
|
113
|
+
#
|
114
|
+
# {
|
115
|
+
# "interfaces": {
|
116
|
+
# "google.fake.v1.ServiceName": {
|
117
|
+
# "retry_codes": {
|
118
|
+
# "idempotent": ["UNAVAILABLE", "DEADLINE_EXCEEDED"],
|
119
|
+
# "non_idempotent": []
|
120
|
+
# },
|
121
|
+
# "retry_params": {
|
122
|
+
# "default": {
|
123
|
+
# "initial_retry_delay_millis": 100,
|
124
|
+
# "retry_delay_multiplier": 1.2,
|
125
|
+
# "max_retry_delay_millis": 1000,
|
126
|
+
# "initial_rpc_timeout_millis": 2000,
|
127
|
+
# "rpc_timeout_multiplier": 1.5,
|
128
|
+
# "max_rpc_timeout_millis": 30000,
|
129
|
+
# "total_timeout_millis": 45000
|
130
|
+
# }
|
131
|
+
# },
|
132
|
+
# "methods": {
|
133
|
+
# "CreateFoo": {
|
134
|
+
# "retry_codes_name": "idempotent",
|
135
|
+
# "retry_params_name": "default"
|
136
|
+
# },
|
137
|
+
# "Publish": {
|
138
|
+
# "retry_codes_name": "non_idempotent",
|
139
|
+
# "retry_params_name": "default",
|
140
|
+
# "bundling": {
|
141
|
+
# "element_count_threshold": 40,
|
142
|
+
# "element_count_limit": 200,
|
143
|
+
# "request_byte_threshold": 90000,
|
144
|
+
# "request_byte_limit": 100000,
|
145
|
+
# "delay_threshold_millis": 100
|
146
|
+
# }
|
147
|
+
# }
|
148
|
+
# }
|
149
|
+
# }
|
150
|
+
# }
|
151
|
+
# }
|
152
|
+
#
|
153
|
+
# @param service_name [String] The fully-qualified name of this
|
154
|
+
# service, used as a key into the client config file (in the
|
155
|
+
# example above, this value should be
|
156
|
+
# 'google.fake.v1.ServiceName').
|
157
|
+
# @param client_config [Hash] A dictionary parsed from the
|
158
|
+
# standard API client config file.
|
159
|
+
# @param bundling_override [Hash] A dictionary of method names to
|
160
|
+
# BundleOptions override those specified in +client_config+.
|
161
|
+
# @param retry_override [Hash] A dictionary of method names to
|
162
|
+
# RetryOptions that override those specified in +client_config+.
|
163
|
+
# @param retry_names [Hash] A dictionary mapping the strings
|
164
|
+
# referring to response status codes to the Python objects
|
165
|
+
# representing those codes.
|
166
|
+
# @param timeout [Numeric] The timeout parameter for all API calls
|
167
|
+
# in this dictionary.
|
168
|
+
# @param bundle_descriptors [Hash{String => BundleDescriptor}]
|
169
|
+
# A dictionary of method names to BundleDescriptor objects for
|
170
|
+
# methods that are bundling-enabled.
|
171
|
+
# @param page_descriptors [Hash{String => PageDescriptor}] A
|
172
|
+
# dictionary of method names to PageDescriptor objects for
|
173
|
+
# methods that are page streaming-enabled.
|
174
|
+
# @return [CallSettings, nil] A CallSettings, or nil if the
|
175
|
+
# service is not found in the config.
|
176
|
+
def construct_settings(
|
177
|
+
service_name, client_config, bundling_override, retry_override,
|
178
|
+
retry_names, timeout, bundle_descriptors: {}, page_descriptors: {})
|
179
|
+
defaults = {}
|
180
|
+
|
181
|
+
service_config = client_config.fetch('interfaces', {})[service_name]
|
182
|
+
return nil unless service_config
|
183
|
+
|
184
|
+
service_config['methods'].each_pair do |method_name, method_config|
|
185
|
+
snake_name = _upper_camel_to_lower_underscore(method_name)
|
186
|
+
|
187
|
+
bundle_descriptor = bundle_descriptors[snake_name]
|
188
|
+
|
189
|
+
defaults[snake_name] = CallSettings.new(
|
190
|
+
timeout: timeout,
|
191
|
+
retry_options: _construct_retry(
|
192
|
+
method_config,
|
193
|
+
retry_override.fetch(snake_name, :OPTION_INHERIT),
|
194
|
+
service_config['retry_codes'],
|
195
|
+
service_config['retry_params'],
|
196
|
+
retry_names),
|
197
|
+
page_descriptor: page_descriptors[snake_name],
|
198
|
+
bundler: _construct_bundling(
|
199
|
+
method_config,
|
200
|
+
bundling_override.fetch(snake_name, :OPTION_INHERIT),
|
201
|
+
bundle_descriptor),
|
202
|
+
bundle_descriptor: bundle_descriptor)
|
203
|
+
end
|
204
|
+
|
205
|
+
defaults
|
206
|
+
end
|
207
|
+
|
208
|
+
module_function :construct_settings, :_construct_bundling,
|
209
|
+
:_construct_retry, :_upper_camel_to_lower_underscore
|
210
|
+
private_class_method :_construct_bundling, :_construct_retry,
|
211
|
+
:_upper_camel_to_lower_underscore
|
212
|
+
end
|
213
|
+
end
|