aws-sdk-code-generator 0.1.0.pre
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/lib/aws-sdk-code-generator.rb +91 -0
- data/lib/aws-sdk-code-generator/apply_docs.rb +37 -0
- data/lib/aws-sdk-code-generator/code_builder.rb +201 -0
- data/lib/aws-sdk-code-generator/dsl/access_control_statement.rb +23 -0
- data/lib/aws-sdk-code-generator/dsl/attribute_accessor.rb +43 -0
- data/lib/aws-sdk-code-generator/dsl/attribute_reader.rb +11 -0
- data/lib/aws-sdk-code-generator/dsl/attribute_writer.rb +11 -0
- data/lib/aws-sdk-code-generator/dsl/autoload_statement.rb +15 -0
- data/lib/aws-sdk-code-generator/dsl/block_param.rb +11 -0
- data/lib/aws-sdk-code-generator/dsl/class.rb +27 -0
- data/lib/aws-sdk-code-generator/dsl/code_literal.rb +66 -0
- data/lib/aws-sdk-code-generator/dsl/code_object.rb +33 -0
- data/lib/aws-sdk-code-generator/dsl/docstring.rb +36 -0
- data/lib/aws-sdk-code-generator/dsl/eigenclass.rb +15 -0
- data/lib/aws-sdk-code-generator/dsl/extend_statement.rb +12 -0
- data/lib/aws-sdk-code-generator/dsl/formatter.rb +25 -0
- data/lib/aws-sdk-code-generator/dsl/include_statement.rb +17 -0
- data/lib/aws-sdk-code-generator/dsl/main.rb +105 -0
- data/lib/aws-sdk-code-generator/dsl/method.rb +108 -0
- data/lib/aws-sdk-code-generator/dsl/module.rb +167 -0
- data/lib/aws-sdk-code-generator/dsl/option_tag.rb +36 -0
- data/lib/aws-sdk-code-generator/dsl/param.rb +43 -0
- data/lib/aws-sdk-code-generator/dsl/param_list.rb +38 -0
- data/lib/aws-sdk-code-generator/dsl/return_tag.rb +19 -0
- data/lib/aws-sdk-code-generator/dsl/tag_default.rb +20 -0
- data/lib/aws-sdk-code-generator/dsl/tag_docstring.rb +27 -0
- data/lib/aws-sdk-code-generator/dsl/tag_type.rb +18 -0
- data/lib/aws-sdk-code-generator/errors.rb +30 -0
- data/lib/aws-sdk-code-generator/gem_builder.rb +71 -0
- data/lib/aws-sdk-code-generator/generators/client_api_module.rb +334 -0
- data/lib/aws-sdk-code-generator/generators/client_class.rb +389 -0
- data/lib/aws-sdk-code-generator/generators/client_operation_documentation.rb +166 -0
- data/lib/aws-sdk-code-generator/generators/errors_module.rb +25 -0
- data/lib/aws-sdk-code-generator/generators/resource/action.rb +88 -0
- data/lib/aws-sdk-code-generator/generators/resource/batch_builder.rb +211 -0
- data/lib/aws-sdk-code-generator/generators/resource/builder.rb +50 -0
- data/lib/aws-sdk-code-generator/generators/resource/client_getter.rb +15 -0
- data/lib/aws-sdk-code-generator/generators/resource/client_request.rb +49 -0
- data/lib/aws-sdk-code-generator/generators/resource/client_request_docs.rb +97 -0
- data/lib/aws-sdk-code-generator/generators/resource/client_request_params.rb +88 -0
- data/lib/aws-sdk-code-generator/generators/resource/collection_class.rb +180 -0
- data/lib/aws-sdk-code-generator/generators/resource/data_attribute_getter.rb +24 -0
- data/lib/aws-sdk-code-generator/generators/resource/data_loaded_method.rb +18 -0
- data/lib/aws-sdk-code-generator/generators/resource/data_method.rb +49 -0
- data/lib/aws-sdk-code-generator/generators/resource/exists_method.rb +29 -0
- data/lib/aws-sdk-code-generator/generators/resource/extract_identifier_method.rb +32 -0
- data/lib/aws-sdk-code-generator/generators/resource/has_association.rb +101 -0
- data/lib/aws-sdk-code-generator/generators/resource/has_many_association.rb +108 -0
- data/lib/aws-sdk-code-generator/generators/resource/identifier_getter.rb +26 -0
- data/lib/aws-sdk-code-generator/generators/resource/identifiers_method.rb +28 -0
- data/lib/aws-sdk-code-generator/generators/resource/initialize_method.rb +67 -0
- data/lib/aws-sdk-code-generator/generators/resource/load_method.rb +65 -0
- data/lib/aws-sdk-code-generator/generators/resource/value_source.rb +68 -0
- data/lib/aws-sdk-code-generator/generators/resource/waiter_method.rb +61 -0
- data/lib/aws-sdk-code-generator/generators/resource_class.rb +325 -0
- data/lib/aws-sdk-code-generator/generators/response_structure_example.rb +83 -0
- data/lib/aws-sdk-code-generator/generators/root_resource_class.rb +42 -0
- data/lib/aws-sdk-code-generator/generators/service_documentation.rb +64 -0
- data/lib/aws-sdk-code-generator/generators/shared_example.rb +132 -0
- data/lib/aws-sdk-code-generator/generators/structure_type_class.rb +95 -0
- data/lib/aws-sdk-code-generator/generators/syntax_example.rb +169 -0
- data/lib/aws-sdk-code-generator/generators/types_module.rb +52 -0
- data/lib/aws-sdk-code-generator/generators/waiter_class.rb +62 -0
- data/lib/aws-sdk-code-generator/generators/waiters_module.rb +20 -0
- data/lib/aws-sdk-code-generator/hash_formatter.rb +122 -0
- data/lib/aws-sdk-code-generator/helper.rb +215 -0
- data/lib/aws-sdk-code-generator/service.rb +126 -0
- data/lib/aws-sdk-code-generator/underscore.rb +45 -0
- data/lib/aws-sdk-code-generator/view.rb +23 -0
- data/lib/aws-sdk-code-generator/views.rb +3 -0
- data/lib/aws-sdk-code-generator/views/features/env.rb +24 -0
- data/lib/aws-sdk-code-generator/views/features/step_definitions.rb +20 -0
- data/lib/aws-sdk-code-generator/views/gemspec.rb +41 -0
- data/lib/aws-sdk-code-generator/views/service_module.rb +85 -0
- data/lib/aws-sdk-code-generator/views/spec/spec_helper.rb +24 -0
- data/lib/aws-sdk-code-generator/views/version.rb +16 -0
- metadata +120 -0
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
module AwsSdkCodeGenerator
|
|
2
|
+
module Generators
|
|
3
|
+
class ClientApiModule < Dsl::Module
|
|
4
|
+
|
|
5
|
+
include Helper
|
|
6
|
+
|
|
7
|
+
SKIP_TRAITS = Set.new(%w(shape deprecated location locationName documentation))
|
|
8
|
+
|
|
9
|
+
SHAPE_CLASSES = {
|
|
10
|
+
'blob' => 'BlobShape',
|
|
11
|
+
'byte' => 'IntegerShape',
|
|
12
|
+
'boolean' => 'BooleanShape',
|
|
13
|
+
'character' => 'StringShape',
|
|
14
|
+
'double' => 'FloatShape',
|
|
15
|
+
'float' => 'FloatShape',
|
|
16
|
+
'integer' => 'IntegerShape',
|
|
17
|
+
'list' => 'ListShape',
|
|
18
|
+
'long' => 'IntegerShape',
|
|
19
|
+
'map' => 'MapShape',
|
|
20
|
+
'string' => 'StringShape',
|
|
21
|
+
'structure' => 'StructureShape',
|
|
22
|
+
'timestamp' => 'TimestampShape',
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
SHAPE_KEYS = {
|
|
26
|
+
# keep
|
|
27
|
+
'flattened' => true,
|
|
28
|
+
'timestampFormat' => true, # glacier api customization
|
|
29
|
+
'xmlNamespace' => true,
|
|
30
|
+
# ignore
|
|
31
|
+
'box' => false,
|
|
32
|
+
'fault' => false,
|
|
33
|
+
'error' => false,
|
|
34
|
+
'deprecated' => false,
|
|
35
|
+
'type' => false,
|
|
36
|
+
'documentation' => false,
|
|
37
|
+
'members' => false,
|
|
38
|
+
'member' => false,
|
|
39
|
+
'key' => false,
|
|
40
|
+
'locationName' => false,
|
|
41
|
+
'value' => false,
|
|
42
|
+
'required' => false,
|
|
43
|
+
'streaming' => false,
|
|
44
|
+
'enum' => false,
|
|
45
|
+
'exception' => false,
|
|
46
|
+
'payload' => false,
|
|
47
|
+
'pattern' => false,
|
|
48
|
+
'sensitive' => false,
|
|
49
|
+
'min' => false,
|
|
50
|
+
'max' => false,
|
|
51
|
+
'wrapper' => false,
|
|
52
|
+
'xmlOrder' => false,
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
METADATA_KEYS = {
|
|
56
|
+
# keep
|
|
57
|
+
'endpointPrefix' => true,
|
|
58
|
+
'signatureVersion' => true,
|
|
59
|
+
'signingName' => true,
|
|
60
|
+
'serviceFullName' => true,
|
|
61
|
+
'protocol' => true,
|
|
62
|
+
'targetPrefix' => true,
|
|
63
|
+
'jsonVersion' => true,
|
|
64
|
+
'errorPrefix' => true,
|
|
65
|
+
'timestampFormat' => true, # glacier api customization
|
|
66
|
+
'xmlNamespace' => true,
|
|
67
|
+
|
|
68
|
+
# ignore
|
|
69
|
+
'apiVersion' => false,
|
|
70
|
+
'checksumFormat' => false,
|
|
71
|
+
'globalEndpoint' => false,
|
|
72
|
+
'serviceAbbreviation' => false,
|
|
73
|
+
'uid' => false,
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
# @option options [required, Hash] :api
|
|
77
|
+
# @option options [required, Hash, nil] :paginators
|
|
78
|
+
def initialize(options)
|
|
79
|
+
@api = options.fetch(:api)
|
|
80
|
+
@paginators = options.fetch(:paginators)
|
|
81
|
+
super('ClientApi')
|
|
82
|
+
docstring("@api private")
|
|
83
|
+
include('Seahorse::Model')
|
|
84
|
+
apply_shape_classes(self)
|
|
85
|
+
apply_shape_definitions(self)
|
|
86
|
+
apply_api_const(self)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
private
|
|
90
|
+
|
|
91
|
+
def metadata
|
|
92
|
+
Dsl::CodeLiteral.new do |c|
|
|
93
|
+
c << "api.metadata = {"
|
|
94
|
+
c.indent do
|
|
95
|
+
(@api['metadata'] || {}).keys.sort.each do |key|
|
|
96
|
+
if METADATA_KEYS[key]
|
|
97
|
+
c << "#{key.inspect} => #{@api['metadata'][key].inspect},"
|
|
98
|
+
elsif METADATA_KEYS[key].nil?
|
|
99
|
+
raise "unhandled metadata key #{key.inspect}"
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
c << "}"
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def operations
|
|
108
|
+
(@api['operations'] || {}).map do |name, operation|
|
|
109
|
+
operation(name, operation)
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def operation(name, operation)
|
|
114
|
+
Dsl::CodeLiteral.new do |code|
|
|
115
|
+
code << "api.add_operation(:#{underscore(name)}, Seahorse::Model::Operation.new.tap do |o|"
|
|
116
|
+
code.indent do |c|
|
|
117
|
+
c << "o.name = #{name.inspect}"
|
|
118
|
+
c << "o.http_method = #{operation['http']['method'].inspect}"
|
|
119
|
+
c << "o.http_request_uri = #{operation['http']['requestUri'].inspect}"
|
|
120
|
+
c << "o.deprecated = true" if operation['deprecated']
|
|
121
|
+
c << "o['authtype'] = #{operation['authtype'].inspect}" if operation['authtype']
|
|
122
|
+
%w(input output).each do |mode|
|
|
123
|
+
c << "o.#{mode} = #{operation_shape_ref(operation[mode])}"
|
|
124
|
+
end
|
|
125
|
+
Array(operation['errors']).each do |error|
|
|
126
|
+
c << "o.errors << #{operation_shape_ref(error)}"
|
|
127
|
+
end
|
|
128
|
+
apply_operation_pager(c, name)
|
|
129
|
+
end
|
|
130
|
+
code << "end)"
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def operation_shape_ref(ref)
|
|
135
|
+
if ref
|
|
136
|
+
metadata = ref.dup
|
|
137
|
+
shape_name = metadata.delete('shape')
|
|
138
|
+
if metadata.empty?
|
|
139
|
+
options = ''
|
|
140
|
+
else
|
|
141
|
+
options = {}
|
|
142
|
+
metadata.each_pair do |key, value|
|
|
143
|
+
next if key == 'resultWrapper'
|
|
144
|
+
if key == 'locationName'
|
|
145
|
+
options[:location_name] = value.inspect
|
|
146
|
+
else
|
|
147
|
+
options[:metadata] ||= {}
|
|
148
|
+
options[:metadata][key] = value.inspect
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
if options.empty?
|
|
152
|
+
options = ''
|
|
153
|
+
else
|
|
154
|
+
options = ', ' + HashFormatter.new(wrap:false).format(options)
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
"Shapes::ShapeRef.new(shape: #{shape_name}#{options})"
|
|
158
|
+
else
|
|
159
|
+
"Shapes::ShapeRef.new(shape: Shapes::StructureShape.new(struct_class: Aws::EmptyStructure))"
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def shape_ref(ref, member_name = nil, required = Set.new)
|
|
164
|
+
line = "Shapes::ShapeRef.new(shape: #{ref['shape']}"
|
|
165
|
+
line += shape_ref_required(required, member_name)
|
|
166
|
+
line += shape_ref_deprecated(ref)
|
|
167
|
+
line += shape_ref_location(ref)
|
|
168
|
+
line += shape_ref_location_name(member_name, ref)
|
|
169
|
+
line += shape_ref_metadata(ref)
|
|
170
|
+
line += ")"
|
|
171
|
+
line
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def apply_operation_pager(code, operation_name)
|
|
175
|
+
if @paginators && @paginators['pagination'][operation_name]
|
|
176
|
+
p = @paginators['pagination'][operation_name]
|
|
177
|
+
input = Array(p['input_token'])
|
|
178
|
+
output = Array(p['output_token'])
|
|
179
|
+
tokens = {}
|
|
180
|
+
input.each.with_index do |key, n|
|
|
181
|
+
tokens[underscore_jmespath(output[n])] = underscore_jmespath(key)
|
|
182
|
+
end
|
|
183
|
+
options = {}
|
|
184
|
+
options[:more_results] = underscore_jmespath(p['more_results']) if p['more_results']
|
|
185
|
+
options[:limit_key] = underscore_jmespath(p['limit_key']) if p['limit_key']
|
|
186
|
+
options[:tokens] = tokens
|
|
187
|
+
options = HashFormatter.new(
|
|
188
|
+
quote_strings: true,
|
|
189
|
+
inline: true,
|
|
190
|
+
wrap: false,
|
|
191
|
+
).format(options)
|
|
192
|
+
code << "o[:pager] = Aws::Pager.new(#{options})" unless tokens.empty?
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
def underscore_jmespath(str)
|
|
197
|
+
Underscore.underscore_jmespath(str)
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
def apply_shape_classes(m)
|
|
201
|
+
m.code do |c|
|
|
202
|
+
shape_defs.each do |shape_name, shape|
|
|
203
|
+
attrs = []
|
|
204
|
+
attrs << "name: '#{shape_name}'"
|
|
205
|
+
shape.each_pair do |key, value|
|
|
206
|
+
if SHAPE_KEYS[key]
|
|
207
|
+
attrs << "#{key}: #{value.inspect}"
|
|
208
|
+
elsif SHAPE_KEYS[key].nil?
|
|
209
|
+
raise "unhandled shape key #{key.inspect}"
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
attrs = attrs.join(', ')
|
|
213
|
+
c << "#{shape_name} = Shapes::#{shape_class(shape['type'])}.new(#{attrs})"
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
def apply_shape_definitions(m)
|
|
219
|
+
m.code do |c|
|
|
220
|
+
shape_defs.each do |shape_name, shape|
|
|
221
|
+
if shape['type'] == 'structure' && !shape['error'] && !shape['exception']
|
|
222
|
+
required = Set.new(shape['required'] || [])
|
|
223
|
+
shape['members'].each do |member_name, member_ref|
|
|
224
|
+
c << "#{shape_name}.add_member(:#{underscore(member_name)}, #{shape_ref(member_ref, member_name, required)})"
|
|
225
|
+
end
|
|
226
|
+
c << "#{shape_name}.struct_class = Types::#{shape_name}"
|
|
227
|
+
if payload = shape['payload']
|
|
228
|
+
c << "#{shape_name}[:payload] = :#{underscore(payload)}"
|
|
229
|
+
c << "#{shape_name}[:payload_member] = #{shape_name}.member(:#{underscore(payload)})"
|
|
230
|
+
end
|
|
231
|
+
elsif shape['type'] == 'list'
|
|
232
|
+
c << "#{shape_name}.member = #{shape_ref(shape['member'])}"
|
|
233
|
+
elsif shape['type'] == 'map'
|
|
234
|
+
c << "#{shape_name}.key = #{shape_ref(shape['key'])}"
|
|
235
|
+
c << "#{shape_name}.value = #{shape_ref(shape['value'])}"
|
|
236
|
+
else
|
|
237
|
+
next
|
|
238
|
+
end
|
|
239
|
+
c.newline
|
|
240
|
+
end
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
def apply_api_const(m)
|
|
245
|
+
m.code do |c|
|
|
246
|
+
c << "# @api private"
|
|
247
|
+
c << "API = Seahorse::Model::Api.new.tap do |api|"
|
|
248
|
+
c.indent do
|
|
249
|
+
c.newline
|
|
250
|
+
if @api['metadata'] && @api['metadata']['apiVersion']
|
|
251
|
+
c << "api.version = #{@api['metadata']['apiVersion'].inspect}"
|
|
252
|
+
c.newline
|
|
253
|
+
end
|
|
254
|
+
c << metadata
|
|
255
|
+
operations.each do |operation|
|
|
256
|
+
c.newline
|
|
257
|
+
c << operation
|
|
258
|
+
end
|
|
259
|
+
end
|
|
260
|
+
c << "end"
|
|
261
|
+
end
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
def shape_ref_required(required, member_name)
|
|
265
|
+
if required.include?(member_name)
|
|
266
|
+
", required: true"
|
|
267
|
+
else
|
|
268
|
+
""
|
|
269
|
+
end
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
def shape_ref_deprecated(ref)
|
|
273
|
+
if ref['deprecated'] || @api['shapes'][ref['shape']]['deprecated']
|
|
274
|
+
", deprecated: true"
|
|
275
|
+
else
|
|
276
|
+
""
|
|
277
|
+
end
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
def shape_ref_location(ref)
|
|
281
|
+
if ref['location']
|
|
282
|
+
", location: #{ref['location'].inspect}"
|
|
283
|
+
else
|
|
284
|
+
''
|
|
285
|
+
end
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
def shape_ref_location_name(member_name, member_ref)
|
|
289
|
+
location_name = member_ref['locationName']
|
|
290
|
+
location_name ||= member_name unless member_ref['location'] == 'headers'
|
|
291
|
+
location_name ? ", location_name: #{location_name.inspect}" : ""
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
def shape_ref_metadata(member_ref)
|
|
295
|
+
metadata = member_ref.inject({}) do |hash, (key, value)|
|
|
296
|
+
hash[key] = value unless SKIP_TRAITS.include?(key)
|
|
297
|
+
hash
|
|
298
|
+
end
|
|
299
|
+
if metadata.empty?
|
|
300
|
+
""
|
|
301
|
+
else
|
|
302
|
+
", metadata: #{metadata.inspect}"
|
|
303
|
+
end
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
def shape_class(type)
|
|
307
|
+
if SHAPE_CLASSES.key?(type)
|
|
308
|
+
SHAPE_CLASSES[type]
|
|
309
|
+
else
|
|
310
|
+
raise ArgumentError, "unsupported shape type `#{type}'"
|
|
311
|
+
end
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
def shape_defs
|
|
315
|
+
Enumerator.new do |y|
|
|
316
|
+
(@api['shapes'] || {}).keys.sort.each do |shape_name|
|
|
317
|
+
y.yield(shape_name, @api['shapes'][shape_name])
|
|
318
|
+
end
|
|
319
|
+
end
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
def structure_defs
|
|
323
|
+
Enumerator.new do |y|
|
|
324
|
+
shape_defs.each do |shape_name, shape|
|
|
325
|
+
if shape['type'] == 'structure' && !shape['error'] && !shape['exception']
|
|
326
|
+
y.yield(shape_name, shape)
|
|
327
|
+
end
|
|
328
|
+
end
|
|
329
|
+
end
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
end
|
|
333
|
+
end
|
|
334
|
+
end
|
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
require 'set'
|
|
2
|
+
require 'pp'
|
|
3
|
+
require 'seahorse/client/plugin'
|
|
4
|
+
|
|
5
|
+
module AwsSdkCodeGenerator
|
|
6
|
+
module Generators
|
|
7
|
+
class ClientClass < Dsl::Class
|
|
8
|
+
|
|
9
|
+
include Helper
|
|
10
|
+
|
|
11
|
+
# @option options [required, Main, Module] :parent
|
|
12
|
+
# @option options [required, String] :identifier
|
|
13
|
+
# @option options [required, Hash] :api
|
|
14
|
+
# @option options [required, Hash, nil] :waiters
|
|
15
|
+
# @option options [required, Hash, nil] :examples
|
|
16
|
+
# @option options [Hash] :add_plugins ({})
|
|
17
|
+
# @option options [Array<String>] :remove_plugins ([])
|
|
18
|
+
def initialize(options)
|
|
19
|
+
@identifier = options.fetch(:identifier)
|
|
20
|
+
@api = options.fetch(:api)
|
|
21
|
+
@waiters = options.fetch(:waiters)
|
|
22
|
+
@examples = options.fetch(:examples)
|
|
23
|
+
@add_plugins = options.fetch(:add_plugins, {})
|
|
24
|
+
@remove_plugins = options.fetch(:remove_plugins, [])
|
|
25
|
+
@gem_name = options.fetch(:gem_name, nil)
|
|
26
|
+
@gem_version = options.fetch(:gem_version, nil)
|
|
27
|
+
super('Client', extends: 'Seahorse::Client::Base', parent: options.fetch(:parent))
|
|
28
|
+
apply_modules(self)
|
|
29
|
+
apply_identifier(self)
|
|
30
|
+
apply_api(self)
|
|
31
|
+
apply_plugins(self)
|
|
32
|
+
apply_initialize_method(self)
|
|
33
|
+
top("\nAws::Plugins::GlobalConfiguration.add_identifier(:#{@identifier})")
|
|
34
|
+
apply_operations(self)
|
|
35
|
+
apply_waiter_methods(self)
|
|
36
|
+
eigenclass do |eigenclass|
|
|
37
|
+
eigenclass.attr_reader('identifier', api_private: true)
|
|
38
|
+
eigenclass.method('errors_module', api_private: true) do |m|
|
|
39
|
+
m.code('Errors')
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
def client_plugins
|
|
47
|
+
@client_plugins ||= begin
|
|
48
|
+
plugins = {}
|
|
49
|
+
plugins.update(default_plugins)
|
|
50
|
+
plugins.update(signature_plugins)
|
|
51
|
+
plugins.update(protocol_plugins(@api['metadata']['protocol']))
|
|
52
|
+
plugins.update(@add_plugins)
|
|
53
|
+
@remove_plugins.each do |plugin_name|
|
|
54
|
+
plugins.delete(plugin_name)
|
|
55
|
+
end
|
|
56
|
+
plugins.map do |class_name, path|
|
|
57
|
+
path = "./#{path}" unless path[0] == '/'
|
|
58
|
+
Kernel.require(path)
|
|
59
|
+
ClientPlugin.new(
|
|
60
|
+
class_name: class_name,
|
|
61
|
+
options: const_get(class_name).options,
|
|
62
|
+
path: path)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def const_get(class_name)
|
|
68
|
+
const_names = class_name.split('::')
|
|
69
|
+
const_names.inject(Kernel) do |const, const_name|
|
|
70
|
+
const.const_get(const_name)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def documented_plugin_options
|
|
75
|
+
client_plugins.map(&:options).flatten.select(&:documented?).sort_by do |opt|
|
|
76
|
+
[opt.required ? 'a' : 'b', opt.name]
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def apply_modules(klass)
|
|
81
|
+
klass.include('Aws::ClientStubs')
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def apply_identifier(klass)
|
|
85
|
+
klass.code do |c|
|
|
86
|
+
c << "@identifier = :#{@identifier}"
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def apply_api(klass)
|
|
91
|
+
klass.code("set_api(ClientApi::API)")
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def apply_plugins(klass)
|
|
95
|
+
klass.code do |c|
|
|
96
|
+
client_plugins.each do |plugin|
|
|
97
|
+
klass.top("require '#{plugin.require_path}'")
|
|
98
|
+
c << "add_plugin(#{plugin.class_name})"
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def apply_initialize_method(klass)
|
|
104
|
+
klass.method(:initialize) do |m|
|
|
105
|
+
documented_plugin_options.each do |option|
|
|
106
|
+
m.option(
|
|
107
|
+
name: option.name,
|
|
108
|
+
type: option.doc_type,
|
|
109
|
+
required: option.required,
|
|
110
|
+
default: option.doc_default,
|
|
111
|
+
docstring: option.docstring
|
|
112
|
+
)
|
|
113
|
+
end
|
|
114
|
+
m.param('*args')
|
|
115
|
+
m.code('super')
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def apply_operations(klass)
|
|
120
|
+
code('# @!group API Operations')
|
|
121
|
+
(@api['operations'] || {}).each do |operation_name, operation|
|
|
122
|
+
method_name = underscore(operation_name)
|
|
123
|
+
klass.method(method_name) do |m|
|
|
124
|
+
ClientOperationDocumentation.apply(
|
|
125
|
+
api: @api,
|
|
126
|
+
service_identifier: @identifier,
|
|
127
|
+
operation_name: operation_name,
|
|
128
|
+
operation: operation,
|
|
129
|
+
examples: @examples,
|
|
130
|
+
method: m
|
|
131
|
+
)
|
|
132
|
+
m.param('params', type: Hash, default: {})
|
|
133
|
+
m.param('options', type: Hash, default: {}, documented: false)
|
|
134
|
+
m.code do |c|
|
|
135
|
+
c << "req = build_request(:#{method_name}, params)"
|
|
136
|
+
c << "req.send_request(options)"
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
code('# @!endgroup')
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
if @gem_name && @gem_version
|
|
144
|
+
gem_version = "\ncontext[:gem_name] = '#{@gem_name}'"
|
|
145
|
+
gem_version += "\ncontext[:gem_version] = '#{@gem_version}'"
|
|
146
|
+
end
|
|
147
|
+
klass.method(:build_request, api_private: true) do |m|
|
|
148
|
+
m.param(:operation_name)
|
|
149
|
+
m.param(:params, default: {})
|
|
150
|
+
m.code(<<-CODE)
|
|
151
|
+
handlers = @handlers.for(operation_name)
|
|
152
|
+
context = Seahorse::Client::RequestContext.new(
|
|
153
|
+
operation_name: operation_name,
|
|
154
|
+
operation: config.api.operation(operation_name),
|
|
155
|
+
client: self,
|
|
156
|
+
params: params,
|
|
157
|
+
config: config)#{gem_version}
|
|
158
|
+
Seahorse::Client::Request.new(handlers, context)
|
|
159
|
+
CODE
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def apply_waiter_methods(klass)
|
|
164
|
+
|
|
165
|
+
# wait_until(waiter_name, params = {}, options = {}, &block)
|
|
166
|
+
if @waiters
|
|
167
|
+
klass.add(Dsl::Method.new('wait_until') do |m|
|
|
168
|
+
m.param('waiter_name', type: Symbol)
|
|
169
|
+
m.param('params', type: Hash, default: {})
|
|
170
|
+
m.param('options', type: Hash, default: {})
|
|
171
|
+
m.option(name: 'max_attempts', type: Integer)
|
|
172
|
+
m.option(name: 'delay', type: Integer)
|
|
173
|
+
m.option(name: 'before_attempt', type: Proc)
|
|
174
|
+
m.option(name: 'before_wait', type: Proc)
|
|
175
|
+
m.docstring(<<-DOCS)
|
|
176
|
+
Polls an API operation until a resource enters a desired state.
|
|
177
|
+
|
|
178
|
+
## Basic Usage
|
|
179
|
+
|
|
180
|
+
A waiter will call an API operation until:
|
|
181
|
+
|
|
182
|
+
* It is successful
|
|
183
|
+
* It enters a terminal state
|
|
184
|
+
* It makes the maximum number of attempts
|
|
185
|
+
|
|
186
|
+
In between attempts, the waiter will sleep.
|
|
187
|
+
|
|
188
|
+
# polls in a loop, sleeping between attempts
|
|
189
|
+
client.waiter_until(waiter_name, params)
|
|
190
|
+
|
|
191
|
+
## Configuration
|
|
192
|
+
|
|
193
|
+
You can configure the maximum number of polling attempts, and the
|
|
194
|
+
delay (in seconds) between each polling attempt. You can pass
|
|
195
|
+
configuration as the final arguments hash.
|
|
196
|
+
|
|
197
|
+
# poll for ~25 seconds
|
|
198
|
+
client.wait_until(waiter_name, params, {
|
|
199
|
+
max_attempts: 5,
|
|
200
|
+
delay: 5,
|
|
201
|
+
})
|
|
202
|
+
|
|
203
|
+
## Callbacks
|
|
204
|
+
|
|
205
|
+
You can be notified before each polling attempt and before each
|
|
206
|
+
delay. If you throw `:success` or `:failure` from these callbacks,
|
|
207
|
+
it will terminate the waiter.
|
|
208
|
+
|
|
209
|
+
started_at = Time.now
|
|
210
|
+
client.wait_until(waiter_name, params, {
|
|
211
|
+
|
|
212
|
+
# disable max attempts
|
|
213
|
+
max_attempts: nil,
|
|
214
|
+
|
|
215
|
+
# poll for 1 hour, instead of a number of attempts
|
|
216
|
+
before_wait: -> (attempts, response) do
|
|
217
|
+
throw :failure if Time.now - started_at > 3600
|
|
218
|
+
end
|
|
219
|
+
})
|
|
220
|
+
|
|
221
|
+
## Handling Errors
|
|
222
|
+
|
|
223
|
+
When a waiter is unsuccessful, it will raise an error.
|
|
224
|
+
All of the failure errors extend from
|
|
225
|
+
{Aws::Waiters::Errors::WaiterFailed}.
|
|
226
|
+
|
|
227
|
+
begin
|
|
228
|
+
client.wait_until(...)
|
|
229
|
+
rescue Aws::Waiters::Errors::WaiterFailed
|
|
230
|
+
# resource did not enter the desired state in time
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
## Valid Waiters
|
|
234
|
+
|
|
235
|
+
The following table lists the valid waiter names, the operations they call,
|
|
236
|
+
and the default `:delay` and `:max_attempts` values.
|
|
237
|
+
|
|
238
|
+
#{waiter_table}
|
|
239
|
+
|
|
240
|
+
@raise [Errors::FailureStateError] Raised when the waiter terminates
|
|
241
|
+
because the waiter has entered a state that it will not transition
|
|
242
|
+
out of, preventing success.
|
|
243
|
+
|
|
244
|
+
@raise [Errors::TooManyAttemptsError] Raised when the configured
|
|
245
|
+
maximum number of attempts have been made, and the waiter is not
|
|
246
|
+
yet successful.
|
|
247
|
+
|
|
248
|
+
@raise [Errors::UnexpectedError] Raised when an error is encounted
|
|
249
|
+
while polling for a resource that is not expected.
|
|
250
|
+
|
|
251
|
+
@raise [Errors::NoSuchWaiterError] Raised when you request to wait
|
|
252
|
+
for an unknown state.
|
|
253
|
+
|
|
254
|
+
@return [Boolean] Returns `true` if the waiter was successful.
|
|
255
|
+
DOCS
|
|
256
|
+
m.code(<<-CODE)
|
|
257
|
+
w = waiter(waiter_name, options)
|
|
258
|
+
yield(w.waiter) if block_given? # deprecated
|
|
259
|
+
w.wait(params)
|
|
260
|
+
CODE
|
|
261
|
+
end)
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
# waiter_names
|
|
265
|
+
klass.add(Dsl::Method.new('waiter_names') do |m|
|
|
266
|
+
m.docstring("@api private")
|
|
267
|
+
m.docstring("@deprecated")
|
|
268
|
+
if @waiters
|
|
269
|
+
m.code("waiters.keys")
|
|
270
|
+
else
|
|
271
|
+
m.code('[]')
|
|
272
|
+
end
|
|
273
|
+
end)
|
|
274
|
+
|
|
275
|
+
# private: waiter(waiter_name)
|
|
276
|
+
klass.add(Dsl::Method.new('waiter', access: :private) do |m|
|
|
277
|
+
m.param('waiter_name', type: Symbol)
|
|
278
|
+
m.param('options', type: Hash, default: {})
|
|
279
|
+
m.code(<<-CODE)
|
|
280
|
+
waiter_class = waiters[waiter_name]
|
|
281
|
+
if waiter_class
|
|
282
|
+
waiter_class.new(options.merge(client: self))
|
|
283
|
+
else
|
|
284
|
+
raise Aws::Waiters::Errors::NoSuchWaiterError.new(waiter_name, waiters.keys)
|
|
285
|
+
end
|
|
286
|
+
CODE
|
|
287
|
+
end) if @waiters
|
|
288
|
+
|
|
289
|
+
# private: waiters
|
|
290
|
+
klass.add(Dsl::Method.new('waiters', access: :private) do |m|
|
|
291
|
+
waiters = {}
|
|
292
|
+
((@waiters || {})['waiters'] || {}).each_pair do |name, definition|
|
|
293
|
+
class_name = "Waiters::#{name}"
|
|
294
|
+
waiters[underscore(name).to_sym] = class_name
|
|
295
|
+
end
|
|
296
|
+
m.code(HashFormatter.new.format(waiters))
|
|
297
|
+
end) if @waiters
|
|
298
|
+
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
def core_lib
|
|
302
|
+
# TODO : may need to register the default plugins directory rather
|
|
303
|
+
# than have the hard-coded here as a relative path
|
|
304
|
+
File.expand_path('../../../../../../gems/aws-sdk-core/lib', __FILE__)
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
def core_plugins
|
|
308
|
+
"#{core_lib}/aws-sdk-core/plugins"
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
def seahorse_plugins
|
|
312
|
+
"#{core_lib}/seahorse/client/plugins"
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
def default_plugins
|
|
316
|
+
{
|
|
317
|
+
'Seahorse::Client::Plugins::ContentLength' => "#{seahorse_plugins}/content_length.rb",
|
|
318
|
+
'Aws::Plugins::CredentialsConfiguration' => "#{core_plugins}/credentials_configuration.rb",
|
|
319
|
+
'Aws::Plugins::Logging' => "#{core_plugins}/logging.rb",
|
|
320
|
+
'Aws::Plugins::ParamConverter' => "#{core_plugins}/param_converter.rb",
|
|
321
|
+
'Aws::Plugins::ParamValidator' => "#{core_plugins}/param_validator.rb",
|
|
322
|
+
'Aws::Plugins::UserAgent' => "#{core_plugins}/user_agent.rb",
|
|
323
|
+
'Aws::Plugins::HelpfulSocketErrors' => "#{core_plugins}/helpful_socket_errors.rb",
|
|
324
|
+
'Aws::Plugins::RetryErrors' => "#{core_plugins}/retry_errors.rb",
|
|
325
|
+
'Aws::Plugins::GlobalConfiguration' => "#{core_plugins}/global_configuration.rb",
|
|
326
|
+
'Aws::Plugins::RegionalEndpoint' => "#{core_plugins}/regional_endpoint.rb",
|
|
327
|
+
'Aws::Plugins::ResponsePaging' => "#{core_plugins}/response_paging.rb",
|
|
328
|
+
'Aws::Plugins::StubResponses' => "#{core_plugins}/stub_responses.rb",
|
|
329
|
+
'Aws::Plugins::IdempotencyToken' => "#{core_plugins}/idempotency_token.rb",
|
|
330
|
+
}
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
def protocol_plugins(protocol)
|
|
334
|
+
{
|
|
335
|
+
'json' => { 'Aws::Plugins::Protocols::JsonRpc' => "#{core_plugins}/protocols/json_rpc.rb" },
|
|
336
|
+
'rest-json' => { 'Aws::Plugins::Protocols::RestJson' => "#{core_plugins}/protocols/rest_json.rb" },
|
|
337
|
+
'rest-xml' => { 'Aws::Plugins::Protocols::RestXml' => "#{core_plugins}/protocols/rest_xml.rb" },
|
|
338
|
+
'query' => { 'Aws::Plugins::Protocols::Query' => "#{core_plugins}/protocols/query.rb" },
|
|
339
|
+
'ec2' => { 'Aws::Plugins::Protocols::EC2' => "#{core_plugins}/protocols/ec2.rb" },
|
|
340
|
+
nil => {}
|
|
341
|
+
}[protocol]
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
def signature_plugins
|
|
345
|
+
case @api['metadata']['signatureVersion']
|
|
346
|
+
when 'v4'
|
|
347
|
+
{ 'Aws::Plugins::SignatureV4' => "#{core_plugins}/signature_v4.rb" }
|
|
348
|
+
when 'v2'
|
|
349
|
+
{ 'Aws::Plugins::SignatureV2' => "#{core_plugins}/signature_v2.rb" }
|
|
350
|
+
else
|
|
351
|
+
{}
|
|
352
|
+
end
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
def waiter_table
|
|
356
|
+
# insert one row for each supported service
|
|
357
|
+
table = []
|
|
358
|
+
@waiters['waiters'].each_pair do |name, waiter|
|
|
359
|
+
table << [underscore(name), "{##{underscore(waiter['operation'])}}", waiter['delay'], waiter['maxAttempts']]
|
|
360
|
+
end
|
|
361
|
+
table = table.sort_by(&:first)
|
|
362
|
+
|
|
363
|
+
# header row
|
|
364
|
+
table.unshift(['waiter_name', 'params', ':delay', ':max_attempts'])
|
|
365
|
+
markdown_table(table)
|
|
366
|
+
end
|
|
367
|
+
|
|
368
|
+
# @api private
|
|
369
|
+
class ClientPlugin
|
|
370
|
+
|
|
371
|
+
def initialize(options)
|
|
372
|
+
@class_name = options.fetch(:class_name)
|
|
373
|
+
@options = options.fetch(:options)
|
|
374
|
+
@require_path = options.fetch(:path).split('/lib/').last
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
# @return [String]
|
|
378
|
+
attr_reader :class_name
|
|
379
|
+
|
|
380
|
+
# @return [Array<Seahorse::Client::Plugin::PluginOption>]
|
|
381
|
+
attr_reader :options
|
|
382
|
+
|
|
383
|
+
# @return [String]
|
|
384
|
+
attr_reader :require_path
|
|
385
|
+
|
|
386
|
+
end
|
|
387
|
+
end
|
|
388
|
+
end
|
|
389
|
+
end
|