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,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
|