aws-sdk-code-generator 0.2.4.pre → 0.4.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 (32) hide show
  1. checksums.yaml +4 -4
  2. data/lib/aws-sdk-code-generator/client_constructor.rb +4 -1
  3. data/lib/aws-sdk-code-generator/client_operation_documentation.rb +10 -4
  4. data/lib/aws-sdk-code-generator/code_builder.rb +78 -9
  5. data/lib/aws-sdk-code-generator/codegenerated_plugin.rb +34 -0
  6. data/lib/aws-sdk-code-generator/gem_builder.rb +4 -5
  7. data/lib/aws-sdk-code-generator/plugin_list.rb +29 -9
  8. data/lib/aws-sdk-code-generator/service.rb +22 -0
  9. data/lib/aws-sdk-code-generator/views/async_client_class.rb +6 -2
  10. data/lib/aws-sdk-code-generator/views/client_api_module.rb +18 -2
  11. data/lib/aws-sdk-code-generator/views/client_class.rb +7 -2
  12. data/lib/aws-sdk-code-generator/views/endpoint_parameters_class.rb +80 -0
  13. data/lib/aws-sdk-code-generator/views/endpoint_provider_class.rb +253 -0
  14. data/lib/aws-sdk-code-generator/views/endpoints_module.rb +177 -0
  15. data/lib/aws-sdk-code-generator/views/endpoints_plugin.rb +85 -0
  16. data/lib/aws-sdk-code-generator/views/gemspec.rb +4 -0
  17. data/lib/aws-sdk-code-generator/views/service_module.rb +15 -1
  18. data/lib/aws-sdk-code-generator/views/spec/endpoint_provider_spec_class.rb +198 -0
  19. data/lib/aws-sdk-code-generator/views/spec/spec_helper.rb +9 -4
  20. data/lib/aws-sdk-code-generator/views/types_module.rb +8 -7
  21. data/lib/aws-sdk-code-generator.rb +6 -0
  22. data/templates/async_client_class.mustache +2 -2
  23. data/templates/client_api_module.mustache +7 -0
  24. data/templates/client_class.mustache +4 -1
  25. data/templates/endpoint_parameters_class.mustache +50 -0
  26. data/templates/endpoint_provider_class.mustache +12 -0
  27. data/templates/endpoints_module.mustache +33 -0
  28. data/templates/endpoints_plugin.mustache +76 -0
  29. data/templates/gemspec.mustache +1 -1
  30. data/templates/spec/endpoint_provider_spec_class.mustache +76 -0
  31. data/templates/spec/spec_helper.mustache +5 -0
  32. metadata +14 -3
@@ -0,0 +1,253 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AwsSdkCodeGenerator
4
+ module Views
5
+ class EndpointProviderClass < View
6
+
7
+ # @option options [required, Service] :service
8
+ # @option options [required, Hash] :endpoint_rules
9
+ def initialize(options)
10
+ @service = options.fetch(:service)
11
+ @endpoint_rules = @service.endpoint_rules
12
+
13
+ version = @endpoint_rules['version']
14
+ return if version&.match(/^\d+\.\d+$/) # && version == '1.0'
15
+
16
+ raise 'Endpoint Rules version must be 1.0'
17
+ end
18
+
19
+ # @return [String|nil]
20
+ def generated_src_warning
21
+ return if @service.protocol == 'api-gateway'
22
+ GENERATED_SRC_WARNING
23
+ end
24
+
25
+ def module_name
26
+ @service.module_name
27
+ end
28
+
29
+ def endpoint_rules_code
30
+ res = StringIO.new
31
+ # map parameters first
32
+ @endpoint_rules["parameters"].each do |k,_v|
33
+ res << indent("#{underscore(k)} = parameters.#{underscore(k)}\n", 3)
34
+ end
35
+
36
+ # map rules
37
+ @endpoint_rules["rules"].each do |rule|
38
+ case rule["type"]
39
+ when "endpoint"
40
+ res << endpoint_rule(rule, 3)
41
+ when "error"
42
+ res << error_rule(rule, 3)
43
+ when "tree"
44
+ res << tree_rule(rule, 3)
45
+ else
46
+ raise "Unknown rule type: #{rule['type']}"
47
+ end
48
+ end
49
+
50
+ # if no rules match, raise an error
51
+ res << indent("raise ArgumentError, 'No endpoint could be resolved'\n", 3)
52
+
53
+ res.string
54
+ end
55
+
56
+ private
57
+
58
+ def endpoint_rule(rule, levels=3)
59
+ res = StringIO.new
60
+ if rule['conditions'] && !rule['conditions'].empty?
61
+ res << conditions(rule['conditions'], levels)
62
+ res << endpoint(rule['endpoint'], levels+1)
63
+ res << indent("end\n", levels)
64
+ else
65
+ res << endpoint(rule['endpoint'], levels)
66
+ end
67
+ res.string
68
+ end
69
+
70
+ def endpoint(endpoint, levels)
71
+ res = StringIO.new
72
+ res << "return Aws::Endpoints::Endpoint.new(url: #{str(endpoint['url'])}"
73
+ if endpoint['headers']
74
+ res << ", headers: #{templated_hash_to_s(endpoint['headers'])}"
75
+ end
76
+ if endpoint['properties']
77
+ res << ", properties: #{templated_hash_to_s(endpoint['properties'])}"
78
+ end
79
+ res << ")\n"
80
+ indent(res.string, levels)
81
+ end
82
+
83
+ def templated_hash_to_s(hash)
84
+ template_hash_values(hash).to_s.gsub('\#{', '#{') # unescape references
85
+ end
86
+
87
+ def template_hash_values(hash)
88
+ hash.inject({}) { |h, (k, v)| h[k] = template_hash_value(v); h }
89
+ end
90
+
91
+ def template_hash_value(value)
92
+ case value
93
+ when Hash
94
+ template_hash_values(value)
95
+ when Array
96
+ value.map { |v| template_hash_value(v) }
97
+ when String
98
+ template_str(value, false)
99
+ else
100
+ value
101
+ end
102
+ end
103
+
104
+ def error_rule(rule, levels=3)
105
+ res = StringIO.new
106
+ if rule['conditions'] && !rule['conditions'].empty?
107
+ res << conditions(rule['conditions'], levels)
108
+ res << error(rule['error'], levels+1)
109
+ res << indent("end\n", levels)
110
+ else
111
+ res << error(rule['error'], levels)
112
+ end
113
+ res.string
114
+ end
115
+
116
+ def error(error, levels)
117
+ indent("raise ArgumentError, #{str(error)}\n", levels)
118
+ end
119
+
120
+ def tree_rule(rule, levels=3)
121
+ res = StringIO.new
122
+ if rule['conditions'] && !rule['conditions'].empty?
123
+ res << conditions(rule['conditions'], levels)
124
+ res << tree_rules(rule['rules'], levels+1)
125
+ res << indent("end\n", levels)
126
+ else
127
+ res << tree_rules(rule['rules'], levels)
128
+ end
129
+ res.string
130
+ end
131
+
132
+ def tree_rules(rules, levels)
133
+ res = StringIO.new
134
+ rules.each do |rule|
135
+ case rule["type"]
136
+ when "endpoint"
137
+ res << endpoint_rule(rule, levels)
138
+ when "error"
139
+ res << error_rule(rule, levels)
140
+ when "tree"
141
+ res << tree_rule(rule, levels)
142
+ else
143
+ raise "Unknown rule type: #{rule['type']}"
144
+ end
145
+ end
146
+ res.string
147
+ end
148
+
149
+ def conditions(conditions, level)
150
+ res = StringIO.new
151
+ cnd_str = conditions.map { |c| condition(c) }.join(" && ")
152
+ res << indent("if #{cnd_str}\n", level)
153
+ res.string
154
+ end
155
+
156
+ def condition(condition)
157
+ if condition['assign']
158
+ "(#{underscore(condition['assign'])} = #{fn(condition)})"
159
+ else
160
+ fn(condition)
161
+ end
162
+ end
163
+
164
+ def str(s)
165
+ if s.is_a?(Hash)
166
+ if s['ref']
167
+ underscore(s['ref'])
168
+ elsif s['fn']
169
+ fn(s)
170
+ else
171
+ raise "Unknown string type: #{s}"
172
+ end
173
+ else
174
+ template_str(s)
175
+ end
176
+ end
177
+
178
+ def template_str(string, wrap=true)
179
+ string.scan(/\{.+?\}/).each do |capture|
180
+ value = capture[1..-2] # strips curly brackets
181
+ string = string.gsub(capture, '#{' + template_replace(value) + '}')
182
+ end
183
+ string = string.gsub('"', '\"')
184
+ wrap ? "\"#{string}\"" : string
185
+ end
186
+
187
+ def template_replace(value)
188
+ indexes = value.split("#")
189
+ res = underscore(indexes.shift)
190
+ res += indexes.map do |index|
191
+ "['#{index}']"
192
+ end.join("")
193
+ res
194
+ end
195
+
196
+ def fn(fn)
197
+ args = fn['argv'].map {|arg| fn_arg(arg)}.join(", ")
198
+ "#{fn_name(fn['fn'])}(#{args})"
199
+ end
200
+
201
+ def fn_arg(arg)
202
+ if arg.is_a?(Hash)
203
+ if arg['ref']
204
+ underscore(arg['ref'])
205
+ elsif arg['fn']
206
+ fn(arg)
207
+ else
208
+ raise "Unexpected argument type: #{arg}"
209
+ end
210
+ elsif arg.is_a?(String)
211
+ template_str(arg)
212
+ else
213
+ arg
214
+ end
215
+ end
216
+
217
+ def fn_name(fn)
218
+ case fn
219
+ when 'isSet'
220
+ "Aws::Endpoints::Matchers.set?"
221
+ when 'not'
222
+ "Aws::Endpoints::Matchers.not"
223
+ when 'getAttr'
224
+ "Aws::Endpoints::Matchers.attr"
225
+ when 'substring'
226
+ "Aws::Endpoints::Matchers.substring"
227
+ when 'stringEquals'
228
+ "Aws::Endpoints::Matchers.string_equals?"
229
+ when 'booleanEquals'
230
+ "Aws::Endpoints::Matchers.boolean_equals?"
231
+ when 'uriEncode'
232
+ "Aws::Endpoints::Matchers.uri_encode"
233
+ when 'parseURL'
234
+ "Aws::Endpoints::Matchers.parse_url"
235
+ when 'isValidHostLabel'
236
+ "Aws::Endpoints::Matchers.valid_host_label?"
237
+ when 'aws.partition'
238
+ "Aws::Endpoints::Matchers.aws_partition"
239
+ when 'aws.parseArn'
240
+ "Aws::Endpoints::Matchers.aws_parse_arn"
241
+ when 'aws.isVirtualHostableS3Bucket'
242
+ "Aws::Endpoints::Matchers.aws_virtual_hostable_s3_bucket?"
243
+ else
244
+ raise "Function not found: #{@fn}"
245
+ end
246
+ end
247
+
248
+ def indent(s, levels=3)
249
+ (" " * levels) + s
250
+ end
251
+ end
252
+ end
253
+ end
@@ -0,0 +1,177 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AwsSdkCodeGenerator
4
+ module Views
5
+ class EndpointsModule < View
6
+ # @option options [required, Service] :service
7
+ def initialize(options)
8
+ @service = options.fetch(:service)
9
+
10
+ @parameters = @service.endpoint_rules.fetch('parameters', {})
11
+
12
+ @endpoint_classes = @service.api['operations'].each.with_object([]) do
13
+ |(name, op), array|
14
+ array << EndpointClass.new(
15
+ name: name,
16
+ parameters: endpoint_parameters_for_operation(op)
17
+ )
18
+ end
19
+ end
20
+
21
+ # @return [Array<EndpointClass>]
22
+ attr_reader :endpoint_classes
23
+
24
+ # @return [String|nil]
25
+ def generated_src_warning
26
+ return if @service.protocol == 'api-gateway'
27
+ GENERATED_SRC_WARNING
28
+ end
29
+
30
+ def module_name
31
+ @service.module_name
32
+ end
33
+
34
+ class EndpointClass
35
+ def initialize(options)
36
+ @name = options[:name]
37
+ @parameters = options[:parameters]
38
+ end
39
+
40
+ # @return [String]
41
+ attr_reader :name
42
+
43
+ # @return [Array<EndpointParameter>]
44
+ attr_reader :parameters
45
+
46
+ def has_endpoint_built_in?
47
+ parameters.any? { |p| p.param_data['builtIn'] == 'SDK::Endpoint' }
48
+ end
49
+ end
50
+
51
+ class EndpointParameter
52
+ def initialize(options)
53
+ @key = options[:key]
54
+ @value = options[:value]
55
+ @source = options[:source]
56
+ @param_data = options[:param_data]
57
+ end
58
+
59
+ # @return [String]
60
+ attr_accessor :key
61
+
62
+ # @return [String]
63
+ attr_accessor :value
64
+
65
+ # @return [String]
66
+ attr_accessor :source
67
+
68
+ # @return [Hash]
69
+ attr_accessor :param_data
70
+
71
+ def static_string?
72
+ @source == 'staticContextParam' && value.is_a?(String)
73
+ end
74
+ end
75
+
76
+
77
+ private
78
+
79
+ def endpoint_parameters_for_operation(operation)
80
+ @parameters.each.with_object([]) do |(param_name, param_data), endpoint_parameters|
81
+ value, source = endpoint_parameter_value(
82
+ operation, param_name, param_data
83
+ )
84
+
85
+ endpoint_parameters << EndpointParameter.new(
86
+ key: Underscore.underscore(param_name),
87
+ value: value,
88
+ source: source,
89
+ param_data: param_data
90
+ )
91
+ end
92
+ end
93
+
94
+ # Most to least
95
+ # staticContextParams
96
+ # contextParam
97
+ # clientContextParams
98
+ # Built-In Bindings
99
+ # Built-in binding default values
100
+ def endpoint_parameter_value(operation, param_name, param_data)
101
+ value, source = [
102
+ static_context_param(operation, param_name), 'staticContextParam'
103
+ ]
104
+ value, source = [
105
+ context_param_value(operation, param_name), 'contextParam'
106
+ ] unless value
107
+ value, source = [
108
+ client_context_param_value(param_name, param_data),
109
+ 'clientContextParam'
110
+ ] unless value
111
+ value, source = [
112
+ built_in_client_context_param_value(param_data), 'builtIn'
113
+ ] unless value
114
+
115
+ [value || 'nil', source]
116
+ end
117
+
118
+ def client_context_param_value(param_name, param_data)
119
+ if @service.api['clientContextParams']&.key?(param_name) &&
120
+ !param_data['builtIn']
121
+ "context.config.#{Underscore.underscore(param_name)}"
122
+ end
123
+ end
124
+
125
+ def built_in_client_context_param_value(param_data)
126
+ case param_data['builtIn']
127
+ when 'AWS::Region'
128
+ 'context.config.region'
129
+ when 'AWS::UseFIPS'
130
+ 'context.config.use_fips_endpoint'
131
+ when 'AWS::UseDualStack'
132
+ if @service.name == 'S3' || @service.name == 'S3Control'
133
+ 'context[:use_dualstack_endpoint]'
134
+ else
135
+ 'context.config.use_dualstack_endpoint'
136
+ end
137
+ when 'AWS::STS::UseGlobalEndpoint'
138
+ "context.config.sts_regional_endpoints == 'legacy'"
139
+ when 'AWS::S3::UseGlobalEndpoint'
140
+ "context.config.s3_us_east_1_regional_endpoint == 'legacy'"
141
+ when 'AWS::S3::Accelerate'
142
+ if @service.name == 'S3' || @service.name == 'S3Control'
143
+ 'context[:use_accelerate_endpoint]'
144
+ else
145
+ 'context.config.use_accelerate_endpoint'
146
+ end
147
+ when 'AWS::S3::ForcePathStyle'
148
+ 'context.config.force_path_style'
149
+ when 'AWS::S3::UseArnRegion', 'AWS::S3Control::UseArnRegion'
150
+ 'context.config.s3_use_arn_region'
151
+ when 'AWS::S3::DisableMultiRegionAccessPoints'
152
+ 'context.config.s3_disable_multiregion_access_points'
153
+ when 'SDK::Endpoint'
154
+ 'endpoint'
155
+ end
156
+ end
157
+
158
+ def context_param_value(operation, param_name)
159
+ return nil unless operation['input']
160
+
161
+ input_shape = operation['input']['shape']
162
+ members = @service.api['shapes'][input_shape].fetch('members', {})
163
+ members.detect do |(member_name, member)|
164
+ context_param = member.fetch('contextParam', {})
165
+ if context_param.fetch('name', nil) == param_name
166
+ break "context.params[:#{Underscore.underscore(member_name)}]"
167
+ end
168
+ end
169
+ end
170
+
171
+ def static_context_param(operation, param_name)
172
+ operation.fetch('staticContextParams', {})
173
+ .fetch(param_name, {}).fetch('value', nil)
174
+ end
175
+ end
176
+ end
177
+ end
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AwsSdkCodeGenerator
4
+ module Views
5
+ class EndpointsPlugin < View
6
+ # @option options [required, Service] :service
7
+ def initialize(options)
8
+ @service = options.fetch(:service)
9
+ if (client_options = @service.api['clientContextParams'])
10
+ endpoint_parameters = @service.endpoint_rules.fetch('parameters', {})
11
+
12
+ @endpoint_options = client_options.each.with_object([]) do |(name, _data), array|
13
+ param_data = endpoint_parameters[name]
14
+
15
+ next if param_data['builtIn']
16
+
17
+ array << EndpointOption.new(
18
+ name: Underscore.underscore(name),
19
+ docstring: param_data['documentation'],
20
+ doc_type: param_data['type'],
21
+ default: param_data['default']
22
+ )
23
+ end
24
+ end
25
+ @endpoint_classes = @service.api['operations'].each.with_object([]) do
26
+ |(op, _api), array|
27
+ array << EndpointClass.new(
28
+ operation_name: Underscore.underscore(op),
29
+ class_name: op
30
+ )
31
+ end
32
+ end
33
+
34
+ # @return [Array<EndpointClass>]
35
+ attr_reader :endpoint_classes
36
+
37
+ # @return [Array<EndpointOption>, nil]
38
+ attr_reader :endpoint_options
39
+
40
+ # @return [String|nil]
41
+ def generated_src_warning
42
+ return if @service.protocol == 'api-gateway'
43
+ GENERATED_SRC_WARNING
44
+ end
45
+
46
+ def module_name
47
+ @service.module_name
48
+ end
49
+
50
+ class EndpointClass
51
+ def initialize(options)
52
+ @operation_name = options[:operation_name]
53
+ @class_name = options[:class_name]
54
+ end
55
+
56
+ # @return [String]
57
+ attr_reader :operation_name
58
+
59
+ # @return [String]
60
+ attr_reader :class_name
61
+ end
62
+
63
+ class EndpointOption
64
+ def initialize(options)
65
+ @name = options[:name]
66
+ @doc_type = options[:doc_type]
67
+ @default = options[:default].nil? ? 'nil' : options[:default]
68
+ @docstring = options[:docstring]
69
+ end
70
+
71
+ # @return [String]
72
+ attr_reader :name
73
+
74
+ # @return [String]
75
+ attr_reader :doc_type
76
+
77
+ # @return [Boolean,String]
78
+ attr_reader :default
79
+
80
+ # @return [String]
81
+ attr_reader :docstring
82
+ end
83
+ end
84
+ end
85
+ end
@@ -42,6 +42,10 @@ module AwsSdkCodeGenerator
42
42
  @custom ? false : true
43
43
  end
44
44
 
45
+ def files
46
+ ['LICENSE.txt', 'CHANGELOG.md', 'VERSION', 'lib/**/*.rb']
47
+ end
48
+
45
49
  # @return [String]
46
50
  def description
47
51
  if @custom
@@ -11,6 +11,7 @@ module AwsSdkCodeGenerator
11
11
  def initialize(options)
12
12
  @service = options.fetch(:service)
13
13
  @prefix = options.fetch(:prefix)
14
+ @codegenerated_plugins = options.fetch(:codegenerated_plugins)
14
15
  end
15
16
 
16
17
  # @return [String|nil]
@@ -56,7 +57,7 @@ module AwsSdkCodeGenerator
56
57
  # This is required to support backwards compatibility for SSO which was
57
58
  # moved from the aws-sdk-sso gem into aws-sdk-core.
58
59
  def require_core_guard?
59
- name == 'SSO' || name == 'STS'
60
+ @service.included_in_core?
60
61
  end
61
62
 
62
63
  # @return [Array<String>]
@@ -64,10 +65,23 @@ module AwsSdkCodeGenerator
64
65
  paths = Set.new
65
66
  paths << "#{@prefix}/types"
66
67
  paths << "#{@prefix}/client_api"
68
+
69
+ # these must be required before the client
70
+ if @codegenerated_plugins
71
+ paths += @codegenerated_plugins.map { | p| p.path }
72
+ end
73
+
67
74
  paths << "#{@prefix}/client"
68
75
  paths << "#{@prefix}/errors"
69
76
  paths << "#{@prefix}/waiters" if @service.waiters
70
77
  paths << "#{@prefix}/resource"
78
+
79
+ unless @service.legacy_endpoints?
80
+ paths << "#{@prefix}/endpoint_parameters"
81
+ paths << "#{@prefix}/endpoint_provider"
82
+ paths << "#{@prefix}/endpoints"
83
+ end
84
+
71
85
  if @service.resources && @service.resources['resources']
72
86
  @service.resources['resources'].keys.each do |resource_name|
73
87
  path = "#{@prefix}/#{underscore(resource_name)}"