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.
Files changed (78) hide show
  1. checksums.yaml +7 -0
  2. data/lib/aws-sdk-code-generator.rb +91 -0
  3. data/lib/aws-sdk-code-generator/apply_docs.rb +37 -0
  4. data/lib/aws-sdk-code-generator/code_builder.rb +201 -0
  5. data/lib/aws-sdk-code-generator/dsl/access_control_statement.rb +23 -0
  6. data/lib/aws-sdk-code-generator/dsl/attribute_accessor.rb +43 -0
  7. data/lib/aws-sdk-code-generator/dsl/attribute_reader.rb +11 -0
  8. data/lib/aws-sdk-code-generator/dsl/attribute_writer.rb +11 -0
  9. data/lib/aws-sdk-code-generator/dsl/autoload_statement.rb +15 -0
  10. data/lib/aws-sdk-code-generator/dsl/block_param.rb +11 -0
  11. data/lib/aws-sdk-code-generator/dsl/class.rb +27 -0
  12. data/lib/aws-sdk-code-generator/dsl/code_literal.rb +66 -0
  13. data/lib/aws-sdk-code-generator/dsl/code_object.rb +33 -0
  14. data/lib/aws-sdk-code-generator/dsl/docstring.rb +36 -0
  15. data/lib/aws-sdk-code-generator/dsl/eigenclass.rb +15 -0
  16. data/lib/aws-sdk-code-generator/dsl/extend_statement.rb +12 -0
  17. data/lib/aws-sdk-code-generator/dsl/formatter.rb +25 -0
  18. data/lib/aws-sdk-code-generator/dsl/include_statement.rb +17 -0
  19. data/lib/aws-sdk-code-generator/dsl/main.rb +105 -0
  20. data/lib/aws-sdk-code-generator/dsl/method.rb +108 -0
  21. data/lib/aws-sdk-code-generator/dsl/module.rb +167 -0
  22. data/lib/aws-sdk-code-generator/dsl/option_tag.rb +36 -0
  23. data/lib/aws-sdk-code-generator/dsl/param.rb +43 -0
  24. data/lib/aws-sdk-code-generator/dsl/param_list.rb +38 -0
  25. data/lib/aws-sdk-code-generator/dsl/return_tag.rb +19 -0
  26. data/lib/aws-sdk-code-generator/dsl/tag_default.rb +20 -0
  27. data/lib/aws-sdk-code-generator/dsl/tag_docstring.rb +27 -0
  28. data/lib/aws-sdk-code-generator/dsl/tag_type.rb +18 -0
  29. data/lib/aws-sdk-code-generator/errors.rb +30 -0
  30. data/lib/aws-sdk-code-generator/gem_builder.rb +71 -0
  31. data/lib/aws-sdk-code-generator/generators/client_api_module.rb +334 -0
  32. data/lib/aws-sdk-code-generator/generators/client_class.rb +389 -0
  33. data/lib/aws-sdk-code-generator/generators/client_operation_documentation.rb +166 -0
  34. data/lib/aws-sdk-code-generator/generators/errors_module.rb +25 -0
  35. data/lib/aws-sdk-code-generator/generators/resource/action.rb +88 -0
  36. data/lib/aws-sdk-code-generator/generators/resource/batch_builder.rb +211 -0
  37. data/lib/aws-sdk-code-generator/generators/resource/builder.rb +50 -0
  38. data/lib/aws-sdk-code-generator/generators/resource/client_getter.rb +15 -0
  39. data/lib/aws-sdk-code-generator/generators/resource/client_request.rb +49 -0
  40. data/lib/aws-sdk-code-generator/generators/resource/client_request_docs.rb +97 -0
  41. data/lib/aws-sdk-code-generator/generators/resource/client_request_params.rb +88 -0
  42. data/lib/aws-sdk-code-generator/generators/resource/collection_class.rb +180 -0
  43. data/lib/aws-sdk-code-generator/generators/resource/data_attribute_getter.rb +24 -0
  44. data/lib/aws-sdk-code-generator/generators/resource/data_loaded_method.rb +18 -0
  45. data/lib/aws-sdk-code-generator/generators/resource/data_method.rb +49 -0
  46. data/lib/aws-sdk-code-generator/generators/resource/exists_method.rb +29 -0
  47. data/lib/aws-sdk-code-generator/generators/resource/extract_identifier_method.rb +32 -0
  48. data/lib/aws-sdk-code-generator/generators/resource/has_association.rb +101 -0
  49. data/lib/aws-sdk-code-generator/generators/resource/has_many_association.rb +108 -0
  50. data/lib/aws-sdk-code-generator/generators/resource/identifier_getter.rb +26 -0
  51. data/lib/aws-sdk-code-generator/generators/resource/identifiers_method.rb +28 -0
  52. data/lib/aws-sdk-code-generator/generators/resource/initialize_method.rb +67 -0
  53. data/lib/aws-sdk-code-generator/generators/resource/load_method.rb +65 -0
  54. data/lib/aws-sdk-code-generator/generators/resource/value_source.rb +68 -0
  55. data/lib/aws-sdk-code-generator/generators/resource/waiter_method.rb +61 -0
  56. data/lib/aws-sdk-code-generator/generators/resource_class.rb +325 -0
  57. data/lib/aws-sdk-code-generator/generators/response_structure_example.rb +83 -0
  58. data/lib/aws-sdk-code-generator/generators/root_resource_class.rb +42 -0
  59. data/lib/aws-sdk-code-generator/generators/service_documentation.rb +64 -0
  60. data/lib/aws-sdk-code-generator/generators/shared_example.rb +132 -0
  61. data/lib/aws-sdk-code-generator/generators/structure_type_class.rb +95 -0
  62. data/lib/aws-sdk-code-generator/generators/syntax_example.rb +169 -0
  63. data/lib/aws-sdk-code-generator/generators/types_module.rb +52 -0
  64. data/lib/aws-sdk-code-generator/generators/waiter_class.rb +62 -0
  65. data/lib/aws-sdk-code-generator/generators/waiters_module.rb +20 -0
  66. data/lib/aws-sdk-code-generator/hash_formatter.rb +122 -0
  67. data/lib/aws-sdk-code-generator/helper.rb +215 -0
  68. data/lib/aws-sdk-code-generator/service.rb +126 -0
  69. data/lib/aws-sdk-code-generator/underscore.rb +45 -0
  70. data/lib/aws-sdk-code-generator/view.rb +23 -0
  71. data/lib/aws-sdk-code-generator/views.rb +3 -0
  72. data/lib/aws-sdk-code-generator/views/features/env.rb +24 -0
  73. data/lib/aws-sdk-code-generator/views/features/step_definitions.rb +20 -0
  74. data/lib/aws-sdk-code-generator/views/gemspec.rb +41 -0
  75. data/lib/aws-sdk-code-generator/views/service_module.rb +85 -0
  76. data/lib/aws-sdk-code-generator/views/spec/spec_helper.rb +24 -0
  77. data/lib/aws-sdk-code-generator/views/version.rb +16 -0
  78. metadata +120 -0
@@ -0,0 +1,52 @@
1
+ require 'set'
2
+
3
+ module AwsSdkCodeGenerator
4
+ module Generators
5
+ class TypesModule < Dsl::Module
6
+
7
+ include Helper
8
+
9
+ # @option options [required, Hash] :api
10
+ def initialize(options)
11
+ @api = options.fetch(:api)
12
+ super('Types')
13
+ input_shapes = compute_input_shapes(@api)
14
+ structures.each do |shape_name, shape|
15
+ add(StructureTypeClass.new(
16
+ name: shape_name,
17
+ api: @api,
18
+ used_as_input: input_shapes.include?(shape_name)
19
+ ))
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ def compute_input_shapes(api)
26
+ inputs = Set.new
27
+ (api['operations'] || {}).each do |_, operation|
28
+ visit_inputs(operation['input'], inputs) if operation['input']
29
+ end
30
+ inputs
31
+ end
32
+
33
+ def visit_inputs(shape_ref, inputs)
34
+ return if inputs.include?(shape_ref['shape']) # recursion
35
+ inputs << shape_ref['shape']
36
+ s = shape(shape_ref)
37
+ case s['type']
38
+ when 'structure'
39
+ s['members'].each_pair do |_, member_ref|
40
+ visit_inputs(member_ref, inputs)
41
+ end
42
+ when 'list'
43
+ visit_inputs(s['member'], inputs)
44
+ when 'map'
45
+ visit_inputs(s['key'], inputs)
46
+ visit_inputs(s['value'], inputs)
47
+ end
48
+ end
49
+
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,62 @@
1
+ module AwsSdkCodeGenerator
2
+ module Generators
3
+ class WaiterClass < Dsl::Class
4
+
5
+ # @option options [required, String] :waiter_name
6
+ # @option options [required, Hash] :waiter
7
+ def initialize(options)
8
+ waiter = options.fetch(:waiter)
9
+ client_method = underscore(waiter['operation'])
10
+ super(options.fetch(:waiter_name))
11
+ code <<-CODE
12
+ # @param [Hash] options
13
+ # @option options [required, Client] :client
14
+ # @option options [Integer] :max_attempts (#{waiter['maxAttempts']})
15
+ # @option options [Integer] :delay (#{waiter['delay']})
16
+ # @option options [Proc] :before_attempt
17
+ # @option options [Proc] :before_wait
18
+ def initialize(options)
19
+ @client = options.fetch(:client)
20
+ @waiter = Aws::Waiters::Waiter.new({
21
+ max_attempts: #{waiter['maxAttempts']},
22
+ delay: #{waiter['delay']},
23
+ poller: Aws::Waiters::Poller.new(#{poller_args(waiter)} )
24
+ }.merge(options))
25
+ end
26
+
27
+ # @option (see Client##{client_method})
28
+ # @return (see Client##{client_method})
29
+ def wait(params = {})
30
+ @waiter.wait(client: @client, params: params)
31
+ end
32
+
33
+ # @api private
34
+ attr_reader :waiter
35
+ CODE
36
+ end
37
+
38
+ private
39
+
40
+ def poller_args(hash)
41
+ hash = hash.dup
42
+ hash.delete('delay')
43
+ hash.delete('maxAttempts')
44
+ hash[:operation_name] = Underscore.underscore(hash.delete('operation')).to_sym
45
+ hash[:acceptors] = hash.delete('acceptors').map do |acceptor|
46
+ if acceptor['argument']
47
+ acceptor['argument'] = Underscore.underscore_jmespath(acceptor['argument'])
48
+ end
49
+ acceptor
50
+ end
51
+ formatter = HashFormatter.new(
52
+ wrap: false,
53
+ inline: false,
54
+ quote_strings: true
55
+ )
56
+ lines = formatter.format(hash).lines
57
+ lines.map { |line| " #{line}" }.join
58
+ end
59
+
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,20 @@
1
+ module AwsSdkCodeGenerator
2
+ module Generators
3
+ class WaitersModule < Dsl::Module
4
+
5
+ include Helper
6
+
7
+ # @option options [required, Module] :parent
8
+ # @option options [required, Hash, nil] :waiters
9
+ def initialize(options)
10
+ waiters = options.fetch(:waiters)
11
+ super('Waiters', parent: options.fetch(:parent))
12
+ require_statement('aws-sdk-core/waiters')
13
+ waiters['waiters'].each do |waiter_name, waiter|
14
+ add(WaiterClass.new(waiter_name: waiter_name, waiter: waiter))
15
+ end
16
+ end
17
+
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,122 @@
1
+ module AwsSdkCodeGenerator
2
+ class HashFormatter
3
+
4
+ # @option options [Boolean] :wrap (true) When `true`, the formatted hash will
5
+ # be wrapped with curly braces.
6
+ #
7
+ # @option options [Boolean] :inline (false) When `true` the formatted hash will
8
+ # contain no newlines.
9
+ #
10
+ # @option options [Boolean] :quote_strings (false) By default, all hash string values
11
+ # must contain their own quotes. If you pass `true`, then all hash string
12
+ # values will be inspected via `#inspect` which will auto-quote them.
13
+ #
14
+ def initialize(options = {})
15
+ @wrap = options.fetch(:wrap, true)
16
+ @inline = options.fetch(:inline, false)
17
+ @quote_strings = options.fetch(:quote_strings, false)
18
+ end
19
+
20
+ def format(obj)
21
+ result = hash(obj, i:'', inline:@inline)
22
+ result = unwrap(result, obj.size) if !@wrap
23
+ result = result.strip if @inline && result.lines.to_a.length == 1
24
+ result
25
+ end
26
+
27
+ private
28
+
29
+ def value(obj, options)
30
+ i = options.fetch(:i)
31
+ inline = options.fetch(:inline)
32
+ case obj
33
+ when Hash then hash(obj, i:i, inline:inline)
34
+ when Array then array(obj, i:i)
35
+ when String then @quote_strings ? obj.inspect : obj
36
+ when Symbol then obj.inspect
37
+ when Fixnum, true, false then obj.inspect
38
+ else raise ArgumentError, "unsupported value `#{obj.class}'"
39
+ end
40
+ end
41
+
42
+ def hash(hash, options)
43
+ i = options.fetch(:i)
44
+ inline = options.fetch(:inline)
45
+ if hash.empty?
46
+ '{}'
47
+ elsif inline_hash?(hash, inline)
48
+ inline_hash(hash)
49
+ else
50
+ multiline_hash(hash, i:i)
51
+ end
52
+ end
53
+
54
+ def inline_hash(hash)
55
+ "{ #{hash_entry(hash.keys[0], hash.values[0], i:'')} }"
56
+ end
57
+
58
+ def multiline_hash(hash, options)
59
+ i = options.fetch(:i)
60
+ str = "{\n"
61
+ hash.each.with_index do |(key, value), n|
62
+ str += "#{i} #{hash_entry(key, value, i:i)}"
63
+ str += "," unless n == hash.keys.length - 1
64
+ str += "\n"
65
+ end
66
+ str + "#{i}}"
67
+ end
68
+
69
+ def hash_entry(key, value, options)
70
+ i = options.fetch(:i)
71
+ value = value(value, i: i + ' ', inline:false)
72
+ if Symbol === key
73
+ "#{key}: #{value}"
74
+ else
75
+ "#{key.inspect} => #{value}"
76
+ end
77
+ end
78
+
79
+ def array(array, options)
80
+ i = options.fetch(:i)
81
+ if array.empty?
82
+ '[]'
83
+ elsif inline_array?(array)
84
+ "[#{value(array[0], i:i, inline:true)}]"
85
+ else
86
+ format_multiline_array(array, i:i)
87
+ end
88
+ end
89
+
90
+ def format_multiline_array(array, options)
91
+ i = options.fetch(:i)
92
+ str = "[\n"
93
+ array.each.with_index do |value, n|
94
+ str += "#{i} #{value(value, i:i + ' ', inline:true)}"
95
+ str += "," unless n == array.length - 1
96
+ str += "\n"
97
+ end
98
+ str + "#{i}]"
99
+ end
100
+
101
+ def inline_hash?(hash, inline)
102
+ hash.length == 1 && String === hash.values[0] && inline
103
+ end
104
+
105
+ def inline_array?(array)
106
+ array.length == 1# && String === array[0]
107
+ end
108
+
109
+ def unwrap(str, size)
110
+ if @inline || size > 1
111
+ str[1..-2]
112
+ else
113
+ lines = str.lines.to_a
114
+ lines.shift
115
+ lines.pop
116
+ lines = lines.map { |line| line[2..-1] }
117
+ lines.join.rstrip
118
+ end
119
+ end
120
+
121
+ end
122
+ end
@@ -0,0 +1,215 @@
1
+ require 'kramdown'
2
+ require 'set'
3
+
4
+ # disable inline attribute lists
5
+ class Kramdown::Converter::Kramdown
6
+ def ial_for_element(*args)
7
+ nil
8
+ end
9
+ end
10
+
11
+ module AwsSdkCodeGenerator
12
+ module Helper
13
+
14
+ def identifier_type(identifier)
15
+ case identifier['type']
16
+ when nil, 'string' then 'String'
17
+ when 'integer' then 'Integer'
18
+ else
19
+ msg = "unsupported identifier type `#{identifier['type']}'"
20
+ raise ArgumentError, msg
21
+ end
22
+ end
23
+
24
+ # @param [Array<Array<String>>]
25
+ def markdown_table(table)
26
+ # compute the width of each column by scanning for longest values
27
+ column_width = lambda do |col|
28
+ table.map { |row| row[col].size }.max
29
+ end
30
+ widths = [
31
+ column_width.call(0),
32
+ column_width.call(1),
33
+ column_width.call(2),
34
+ column_width.call(3),
35
+ ]
36
+
37
+ # insert a dashed line after the header row
38
+ table = [
39
+ table[0],
40
+ ['-' * widths[0], '-' * widths[1], '-' * widths[2], '-' * widths[3]]
41
+ ] + table[1..-1]
42
+
43
+ # build the final table
44
+ line = "| #{widths.map{|n| "%-#{n}s" }.join(' | ')} |"
45
+ table.map { |row| line % row }.join("\n")
46
+ end
47
+ module_function :markdown_table
48
+
49
+ def underscore(str)
50
+ str.split('.').map do |part|
51
+ Underscore.underscore(part)
52
+ end.join('.')
53
+ end
54
+
55
+ def structures
56
+ Enumerator.new do |y|
57
+ (@api['shapes'] || {}).each do |shape_name, shape|
58
+ if shape['type'] == 'structure' && !shape['error'] && !shape['exception']
59
+ y.yield(shape_name, shape)
60
+ end
61
+ end
62
+ end
63
+ end
64
+
65
+ # @option options [Boolean] :nested (false)
66
+ def ruby_input_type(shape_ref, options = {})
67
+ nested = options.fetch(:nested, false)
68
+ shape = @api['shapes'][shape_ref['shape']]
69
+ case shape['type']
70
+ when 'byte' then 'Integer<byte>'
71
+ when 'blob' then 'String, IO'
72
+ when 'boolean' then 'Boolean'
73
+ when 'character' then 'String<character>'
74
+ when 'double' then 'Float'
75
+ when 'float' then 'Float'
76
+ when 'integer' then 'Integer'
77
+ when 'list'
78
+ if nested
79
+ "Array"
80
+ else
81
+ "Array<#{ruby_input_type(shape['member'], nested:true)}>"
82
+ end
83
+ when 'long' then 'Integer'
84
+ when 'map'
85
+ if nested
86
+ "Hash"
87
+ else
88
+ "Hash<String,#{ruby_input_type(shape['value'], nested:true)}>"
89
+ end
90
+ when 'string' then 'String'
91
+ when 'structure' then "Types::#{shape_ref['shape']}"
92
+ when 'timestamp' then 'Time,DateTime,Date,Integer,String'
93
+ else
94
+ raise "unhandled type #{shape.type}.inspect"
95
+ end
96
+ end
97
+
98
+ def ruby_type(shape_ref)
99
+ shape = @api['shapes'][shape_ref['shape']]
100
+ case shape['type']
101
+ when 'blob' then streaming?(shape_ref, shape) ? 'IO' : 'String'
102
+ when 'boolean' then 'Boolean'
103
+ when 'byte' then 'Integer<byte>'
104
+ when 'character' then 'String<character>'
105
+ when 'double' then 'Float'
106
+ when 'float' then 'Float'
107
+ when 'integer' then 'Integer'
108
+ when 'list' then "Array<#{ruby_type(shape['member'])}>"
109
+ when 'long' then 'Integer'
110
+ when 'map' then "Hash<String,#{ruby_type(shape['value'])}>"
111
+ when 'string' then 'String'
112
+ when 'structure' then "Types::#{shape_ref['shape']}"
113
+ when 'timestamp' then 'Time'
114
+ else
115
+ raise "unhandled type #{shape['type'].inspect}"
116
+ end
117
+ end
118
+
119
+ def streaming?(ref, shape)
120
+ ref['streaming'] || shape['streaming']
121
+ end
122
+
123
+ # @option options [Integer] :line_width (70)
124
+ def documentation(ref_or_shape, options = {})
125
+ line_width = options.fetch(:line_width, 70)
126
+ shape = ref_or_shape.key?('type') ? ref_or_shape : shape(ref_or_shape)
127
+ docstring = ref_or_shape['documentation'] || shape['documentation'] || ''
128
+
129
+ # append boilerplate idempotency docs
130
+ if ref_or_shape['idempotencyToken']
131
+ docstring = "#{docstring}<p>**A suitable default value is "
132
+ docstring += "auto-generated.** You should normally "
133
+ docstring += "not need to pass this option.</p>"
134
+ docstring = docstring.strip
135
+ end
136
+
137
+ docstring == '' ? docstring : markdown(docstring, line_width:line_width)
138
+ end
139
+
140
+ def member_shape(shape_name, member_name)
141
+ shape = @api['shapes'][shape_name]
142
+ member_ref = shape['members'][member_name]
143
+ shape(member_ref)
144
+ end
145
+
146
+ def shape(ref)
147
+ if ref.nil?
148
+ nil
149
+ else
150
+ shape = @api['shapes'][ref['shape']]
151
+ raise ArgumentError, "no such shape `#{ref['shape']}'" unless shape
152
+ shape
153
+ end
154
+ end
155
+
156
+ # @option options [Integer] :line_width (70)
157
+ def markdown(html, options = {})
158
+ line_width = options.fetch(:line_width, 70)
159
+ # TODO : this section of code is **very slow** and runs many times
160
+ # while building a service.
161
+ if html
162
+ html = "<p>#{html}</p>" unless html.match(/<\w+>/)
163
+
164
+ # unescaped curly braces cause YARD errors, they are interpreted
165
+ # as code links.
166
+ html = html.gsub('{', "\\{").gsub('}', "\\}")
167
+
168
+ # Kramdown generates invalid markup when there are attributes
169
+ # on the code tag, have to reduce these down to get the proper markdown.
170
+ html = html.gsub(/<code.*?>(.+?)<\/code>/) { "<code>#{$1}</code>" }
171
+
172
+ # Kramdown creates invalid markup with target="_blank" attributes.
173
+ html = html.gsub(' target="_blank"', '')
174
+
175
+ # There are quite a few empty <a> tags. These appear to be code names,
176
+ # such as structure member names, or structure type names. We should
177
+ # investigate if it is possible to inflect these properly and then
178
+ # turn them into YARD links.
179
+ html = html.gsub(/<a>(.+?)<\/a>/) { $1 }
180
+
181
+ # <important> tag doesn't render well
182
+ html = html.gsub(/<important>(.+?)<\/important>/){ "<p>#{$1}</p>" }
183
+
184
+ markdown = Kramdown::Document.new(
185
+ html,
186
+ input: 'html',
187
+ line_width: line_width,
188
+ auto_ids: false
189
+ ).to_kramdown.strip
190
+
191
+ # remove extra escape
192
+ markdown.gsub(/\\(\*|`|'|")/, '\1')
193
+ end
194
+ end
195
+
196
+ def deep_copy(obj)
197
+ case obj
198
+ when nil then nil
199
+ when true then true
200
+ when false then false
201
+ when Hash then obj.inject({}) { |h,(k,v)| h[deep_copy(k)] = deep_copy(v); h }
202
+ when Array then obj.map { |v| deep_copy(v) }
203
+ else
204
+ if obj.respond_to?(:dup)
205
+ obj.dup
206
+ elsif obj.respond_to?(:clone)
207
+ obj.clone
208
+ else
209
+ obj
210
+ end
211
+ end
212
+ end
213
+
214
+ end
215
+ end