smithy 2.0.0.pre0 → 2.0.0.pre1

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 (103) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/bin/smithy-ruby +1 -4
  4. data/lib/smithy/command.rb +68 -0
  5. data/lib/smithy/generators/base.rb +19 -0
  6. data/lib/smithy/generators/client.rb +106 -0
  7. data/lib/smithy/generators/schema.rb +61 -0
  8. data/lib/smithy/generators.rb +29 -0
  9. data/lib/smithy/model/flattener.rb +114 -0
  10. data/lib/smithy/model/operation_parser.rb +42 -0
  11. data/lib/smithy/model/rbs.rb +57 -0
  12. data/lib/smithy/model/service_index.rb +51 -0
  13. data/lib/smithy/model/service_parser.rb +74 -0
  14. data/lib/smithy/model/shape.rb +49 -0
  15. data/lib/smithy/model/structure_parser.rb +43 -0
  16. data/lib/smithy/model/yard.rb +100 -0
  17. data/lib/smithy/model.rb +54 -0
  18. data/lib/smithy/plan.rb +79 -3
  19. data/lib/smithy/templates/client/auth_parameters.erb +29 -0
  20. data/lib/smithy/templates/client/auth_parameters_rbs.erb +14 -0
  21. data/lib/smithy/templates/client/auth_plugin.erb +115 -0
  22. data/lib/smithy/templates/client/auth_resolver.erb +16 -0
  23. data/lib/smithy/templates/client/auth_resolver_rbs.erb +5 -0
  24. data/lib/smithy/templates/client/client.erb +142 -0
  25. data/lib/smithy/templates/client/client_rbs.erb +29 -0
  26. data/lib/smithy/templates/client/customizations.erb +3 -0
  27. data/lib/smithy/templates/client/endpoint_parameters.erb +65 -0
  28. data/lib/smithy/templates/client/endpoint_parameters_rbs.erb +13 -0
  29. data/lib/smithy/templates/client/endpoint_plugin.erb +58 -0
  30. data/lib/smithy/templates/client/endpoint_provider.erb +15 -0
  31. data/lib/smithy/templates/client/endpoint_provider_rbs.erb +5 -0
  32. data/lib/smithy/templates/client/endpoint_provider_spec.erb +70 -0
  33. data/lib/smithy/templates/client/errors.erb +69 -0
  34. data/lib/smithy/templates/client/errors_rbs.erb +17 -0
  35. data/lib/smithy/templates/client/gemspec.erb +17 -0
  36. data/lib/smithy/templates/client/module.erb +22 -0
  37. data/lib/smithy/templates/client/module_rbs.erb +7 -0
  38. data/lib/smithy/templates/client/paginators.erb +33 -0
  39. data/lib/smithy/templates/client/protocol_spec.erb +144 -0
  40. data/lib/smithy/templates/client/rubocop_yml.erb +33 -0
  41. data/lib/smithy/templates/client/schema.erb +76 -0
  42. data/lib/smithy/templates/client/schema_rbs.erb +13 -0
  43. data/lib/smithy/templates/client/spec_helper.erb +10 -0
  44. data/lib/smithy/templates/client/types.erb +64 -0
  45. data/lib/smithy/templates/client/types_rbs.erb +47 -0
  46. data/lib/smithy/templates/client/waiters.erb +42 -0
  47. data/lib/smithy/util/hash_formatter.rb +124 -0
  48. data/lib/smithy/util/underscore.rb +18 -0
  49. data/lib/smithy/util.rb +9 -0
  50. data/lib/smithy/views/client/auth_parameter.rb +29 -0
  51. data/lib/smithy/views/client/auth_parameters.rb +23 -0
  52. data/lib/smithy/views/client/auth_parameters_rbs.rb +23 -0
  53. data/lib/smithy/views/client/auth_plugin.rb +35 -0
  54. data/lib/smithy/views/client/auth_resolver.rb +125 -0
  55. data/lib/smithy/views/client/auth_resolver_rbs.rb +19 -0
  56. data/lib/smithy/views/client/client.rb +208 -0
  57. data/lib/smithy/views/client/client_rbs.rb +231 -0
  58. data/lib/smithy/views/client/customizations.rb +10 -0
  59. data/lib/smithy/views/client/endpoint_parameter.rb +156 -0
  60. data/lib/smithy/views/client/endpoint_parameters.rb +43 -0
  61. data/lib/smithy/views/client/endpoint_parameters_rbs.rb +28 -0
  62. data/lib/smithy/views/client/endpoint_plugin.rb +27 -0
  63. data/lib/smithy/views/client/endpoint_provider.rb +241 -0
  64. data/lib/smithy/views/client/endpoint_provider_rbs.rb +19 -0
  65. data/lib/smithy/views/client/endpoint_provider_spec.rb +137 -0
  66. data/lib/smithy/views/client/errors.rb +88 -0
  67. data/lib/smithy/views/client/errors_rbs.rb +12 -0
  68. data/lib/smithy/views/client/gemspec.rb +36 -0
  69. data/lib/smithy/views/client/module.rb +107 -0
  70. data/lib/smithy/views/client/module_rbs.rb +20 -0
  71. data/lib/smithy/views/client/operation_examples.rb +157 -0
  72. data/lib/smithy/views/client/paginators.rb +108 -0
  73. data/lib/smithy/views/client/plugin.rb +29 -0
  74. data/lib/smithy/views/client/plugin_list.rb +57 -0
  75. data/lib/smithy/views/client/protocol_spec.rb +254 -0
  76. data/lib/smithy/views/client/request_response_example.rb +179 -0
  77. data/lib/smithy/views/client/rubocop_yml.rb +19 -0
  78. data/lib/smithy/views/client/schema.rb +356 -0
  79. data/lib/smithy/views/client/schema_rbs.rb +84 -0
  80. data/lib/smithy/views/client/shape_to_hash.rb +99 -0
  81. data/lib/smithy/views/client/spec_helper.rb +19 -0
  82. data/lib/smithy/views/client/types.rb +293 -0
  83. data/lib/smithy/views/client/types_rbs.rb +67 -0
  84. data/lib/smithy/views/client/waiters.rb +82 -0
  85. data/lib/smithy/views/client.rb +47 -0
  86. data/lib/smithy/views/view.rb +30 -0
  87. data/lib/smithy/views.rb +9 -0
  88. data/lib/smithy/weld.rb +109 -0
  89. data/lib/smithy/welds/auth/anonymous_auth.rb +31 -0
  90. data/lib/smithy/welds/auth/http_api_key_auth.rb +34 -0
  91. data/lib/smithy/welds/auth/http_basic_auth.rb +34 -0
  92. data/lib/smithy/welds/auth/http_bearer_auth.rb +34 -0
  93. data/lib/smithy/welds/auth/http_digest_auth.rb +34 -0
  94. data/lib/smithy/welds/plugins.rb +54 -0
  95. data/lib/smithy/welds/rpc_v2_cbor.rb +20 -0
  96. data/lib/smithy/welds/rubocop.rb +33 -0
  97. data/lib/smithy/welds/transforms/default_endpoint_rules.json +35 -0
  98. data/lib/smithy/welds/transforms/default_endpoint_tests.json +24 -0
  99. data/lib/smithy/welds/transforms/endpoints.rb +68 -0
  100. data/lib/smithy/welds/transforms/synthetic_input_output.rb +60 -0
  101. data/lib/smithy/welds.rb +29 -0
  102. data/lib/smithy.rb +33 -2
  103. metadata +144 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: caac54dead11621fd0d7a29869e6f9dd39945a7a2e0a0dea66435e2c17c79ab1
4
- data.tar.gz: b6ec328bb2439cae20f86056326e769b39376301b293ba5438170d105315e5a5
3
+ metadata.gz: a3d6e2e6c2474e08572ffac8b6d9e014d61433a94b27329c04c58ad964f1f647
4
+ data.tar.gz: 50392ce54b2239d61b8a5c1189b9e55bdd8e3c9558f163f1d005b5b91ff85867
5
5
  SHA512:
6
- metadata.gz: d7b62fcc5ce4c8bb2af3f89886ed59b49f26189e923368a27e3d92094a6f58616a0d28d9668a3f2771a21880f8857dec059af3e96c55d4cb8f4fc9061bed2f3c
7
- data.tar.gz: 152b056cddc6c5bffa308da073cb6bb8443ff6fc98780946e1d8409486e59b72b80dd78464feee32de1c520219c3b149ccd77bf7d6e2880aa3bb4e282e86bd12
6
+ metadata.gz: d4b2bc367e168ed6e0841bb6efe7b5b949910efcf36778d4a95afa127935fb758929128cc9d93feae7402a98185e629e16461cbd7f38a3d2910169076b740494
7
+ data.tar.gz: 32bd9986f0dc5a9c9c52414d48c5dcd2f0213d8115d475a4f50dbe6ac375a47a0ce30b5a5a39e815cb716ca3ff2c72c4e4f0ff59181b7699f54c1edc19b6dca0
data/VERSION CHANGED
@@ -1 +1 @@
1
- 2.0.0.pre0
1
+ 2.0.0.pre1
data/bin/smithy-ruby CHANGED
@@ -1,8 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
- puts "I'm being executed!"
5
-
6
4
  require 'smithy'
7
-
8
- Smithy::Plan.new(ARGV)
5
+ Smithy::Command::CLI.start
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ module Smithy
6
+ # @api private
7
+ module Command
8
+ # @api private
9
+ class Base < Thor
10
+ # Necessary to report the correct status to the parent process (`smithy build`)
11
+ def self.exit_on_failure?
12
+ true
13
+ end
14
+ end
15
+
16
+ # @api private
17
+ class Smith < Base
18
+ class_option :destination_root, type: :string, required: true,
19
+ default: ENV.fetch('SMITHY_PLUGIN_DIR', nil),
20
+ desc: 'The destination directory for the generated code.'
21
+ class_option :quiet, type: :boolean, default: false, desc: 'Suppress all output.'
22
+
23
+ def self.gem_options!
24
+ method_option :name, type: :string,
25
+ desc: 'The name of the service to generate code for.' \
26
+ 'Defaults to the name of the first service shape found in the model.'
27
+ method_option :module_name, type: :string,
28
+ desc: 'The module name to generate, e.g. `Organization::Weather`. ' \
29
+ 'Defaults to the name of the service.'
30
+ method_option :gem_name, type: :string, required: true,
31
+ desc: 'The name of the gem to generate. Defaults to a gem name derived ' \
32
+ 'from the module name, suffixed with "-schema" if type is schema.'
33
+ method_option :gem_version, type: :string, required: true, desc: 'The version of the gem to generate.'
34
+ end
35
+
36
+ gem_options!
37
+ desc 'schema', 'Generates a schema for the model provided to STDIN.'
38
+ def schema
39
+ invoke(:schema, options)
40
+ end
41
+
42
+ gem_options!
43
+ desc 'client', 'Generates a client for the model provided to STDIN.'
44
+ def client
45
+ invoke(:client, options)
46
+ end
47
+
48
+ desc 'server', 'Generates a server for the model provided to STDIN.'
49
+ def server
50
+ raise NotImplementedError, 'server generation is not yet implemented'
51
+ end
52
+
53
+ no_tasks do
54
+ def invoke(type, options)
55
+ model = JSON.parse($stdin.read)
56
+ plan = Smithy::Plan.new(model, type, options)
57
+ Smithy.generate(plan)
58
+ end
59
+ end
60
+ end
61
+
62
+ # @api private
63
+ class CLI < Base
64
+ desc 'smith', 'Generate code using a Smithy model.'
65
+ subcommand 'smith', Smith
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Smithy
4
+ module Generators
5
+ # Base class for generating files.
6
+ class Base
7
+ include Thor::Base
8
+ include Thor::Actions
9
+
10
+ # @param [Plan] plan The plan to generate.
11
+ def initialize(plan)
12
+ # Necessary for Thor::Base and Thor::Actions
13
+ self.options = { force: true, quiet: plan.quiet }
14
+ self.destination_root = plan.destination_root
15
+ shell.base = self
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Smithy
4
+ module Generators
5
+ # Generates a gem for the client.
6
+ class Client < Base
7
+ # @param [Plan] plan The plan to generate.
8
+ def initialize(plan)
9
+ @plan = plan
10
+ @gem_name = plan.gem_name
11
+ super
12
+ end
13
+
14
+ def generate
15
+ gem_files.each_with_object([]) do |(file, content), files|
16
+ next if file == "lib/#{@gem_name}/customizations.rb" && should_skip_customizations?
17
+
18
+ create_file file, content
19
+ files << file
20
+ end
21
+ end
22
+
23
+ def source
24
+ source_files.map { |_file, content| content }.join("\n")
25
+ end
26
+
27
+ private
28
+
29
+ # rubocop:disable Metrics/AbcSize
30
+ def gem_files
31
+ Enumerator.new do |e|
32
+ e.yield "#{@gem_name}.gemspec", Views::Client::Gemspec.new(@plan).render
33
+ e.yield '.rubocop.yml', Views::Client::RubocopYml.new(@plan).render
34
+
35
+ source_files.each { |file, content| e.yield file, content }
36
+ e.yield "lib/#{@gem_name}/customizations.rb", Views::Client::Customizations.new.render
37
+
38
+ spec_files.each { |file, content| e.yield file, content }
39
+
40
+ rbs_files.each { |file, content| e.yield file, content }
41
+ end
42
+ end
43
+
44
+ def source_files
45
+ Enumerator.new do |e|
46
+ e.yield "lib/#{@gem_name}.rb", Views::Client::Module.new(@plan).render
47
+ e.yield "lib/#{@gem_name}/auth_parameters.rb", Views::Client::AuthParameters.new(@plan).render
48
+ e.yield "lib/#{@gem_name}/auth_resolver.rb", Views::Client::AuthResolver.new(@plan).render
49
+ e.yield "lib/#{@gem_name}/errors.rb", Views::Client::Errors.new(@plan).render
50
+ e.yield "lib/#{@gem_name}/endpoint_parameters.rb", Views::Client::EndpointParameters.new(@plan).render
51
+ e.yield "lib/#{@gem_name}/endpoint_provider.rb", Views::Client::EndpointProvider.new(@plan).render
52
+ e.yield "lib/#{@gem_name}/paginators.rb", Views::Client::Paginators.new(@plan).render
53
+ code_generated_plugins.each { |path, plugin| e.yield path, plugin.source }
54
+ e.yield "lib/#{@gem_name}/types.rb", Views::Client::Types.new(@plan).render
55
+ e.yield "lib/#{@gem_name}/schema.rb", Views::Client::Schema.new(@plan).render
56
+ e.yield "lib/#{@gem_name}/waiters.rb", Views::Client::Waiters.new(@plan).render
57
+ e.yield "lib/#{@gem_name}/client.rb", Views::Client::Client.new(@plan, code_generated_plugins).render
58
+ end
59
+ end
60
+
61
+ def spec_files
62
+ Enumerator.new do |e|
63
+ e.yield 'spec/spec_helper.rb', Views::Client::SpecHelper.new(@plan).render
64
+ e.yield "spec/#{@gem_name}/endpoint_provider_spec.rb", Views::Client::EndpointProviderSpec.new(@plan).render
65
+ e.yield "spec/#{@gem_name}/protocol_spec.rb", Views::Client::ProtocolSpec.new(@plan).render
66
+ end
67
+ end
68
+
69
+ def rbs_files
70
+ Enumerator.new do |e|
71
+ e.yield "sig/#{@gem_name}.rbs", Views::Client::ModuleRbs.new(@plan).render
72
+ e.yield "sig/#{@gem_name}/auth_parameters.rbs", Views::Client::AuthParametersRbs.new(@plan).render
73
+ e.yield "sig/#{@gem_name}/auth_resolver.rbs", Views::Client::AuthResolverRbs.new(@plan).render
74
+ e.yield "sig/#{@gem_name}/client.rbs", Views::Client::ClientRbs.new(@plan, code_generated_plugins).render
75
+ e.yield "sig/#{@gem_name}/errors.rbs", Views::Client::ErrorsRbs.new(@plan).render
76
+ e.yield "sig/#{@gem_name}/endpoint_parameters.rbs", Views::Client::EndpointParametersRbs.new(@plan).render
77
+ e.yield "sig/#{@gem_name}/endpoint_provider.rbs", Views::Client::EndpointProviderRbs.new(@plan).render
78
+ e.yield "sig/#{@gem_name}/schema.rbs", Views::Client::SchemaRbs.new(@plan).render
79
+ e.yield "sig/#{@gem_name}/types.rbs", Views::Client::TypesRbs.new(@plan).render
80
+ end
81
+ end
82
+ # rubocop:enable Metrics/AbcSize
83
+
84
+ def code_generated_plugins
85
+ Enumerator.new do |e|
86
+ e.yield "lib/#{@gem_name}/plugins/auth.rb", Views::Client::Plugin.new(
87
+ class_name: "::#{@plan.module_name}::Plugins::Auth",
88
+ require_path: 'plugins/auth',
89
+ require_relative: true,
90
+ source: Views::Client::AuthPlugin.new(@plan).render
91
+ )
92
+ e.yield "lib/#{@gem_name}/plugins/endpoint.rb", Views::Client::Plugin.new(
93
+ class_name: "::#{@plan.module_name}::Plugins::Endpoint",
94
+ require_path: 'plugins/endpoint',
95
+ require_relative: true,
96
+ source: Views::Client::EndpointPlugin.new(@plan).render
97
+ )
98
+ end
99
+ end
100
+
101
+ def should_skip_customizations?
102
+ Dir["#{destination_root}/**/*"].any? { |f| f.include?('/customizations.rb') }
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Smithy
4
+ module Generators
5
+ # Generates a gem for the types.
6
+ class Schema < Base
7
+ # @param [Plan] plan The plan to generate.
8
+ def initialize(plan)
9
+ @plan = plan
10
+ @gem_name = plan.gem_name
11
+ super
12
+ end
13
+
14
+ def generate
15
+ gem_files.each_with_object([]) do |(file, content), files|
16
+ next if file == "lib/#{@gem_name}/customizations.rb" && should_skip_customizations?
17
+
18
+ create_file file, content
19
+ files << file
20
+ end
21
+ end
22
+
23
+ def source
24
+ source_files.map { |_file, content| content }.join("\n")
25
+ end
26
+
27
+ private
28
+
29
+ def gem_files
30
+ Enumerator.new do |e|
31
+ e.yield "#{@gem_name}.gemspec", Views::Client::Gemspec.new(@plan).render
32
+ e.yield '.rubocop.yml', Views::Client::RubocopYml.new(@plan).render
33
+
34
+ source_files.each { |file, content| e.yield file, content }
35
+ e.yield "lib/#{@gem_name}/customizations.rb", Views::Client::Customizations.new.render
36
+ rbs_files.each { |file, content| e.yield file, content }
37
+ end
38
+ end
39
+
40
+ def source_files
41
+ Enumerator.new do |e|
42
+ e.yield "lib/#{@gem_name}.rb", Views::Client::Module.new(@plan).render
43
+ e.yield "lib/#{@gem_name}/types.rb", Views::Client::Types.new(@plan).render
44
+ e.yield "lib/#{@gem_name}/schema.rb", Views::Client::Schema.new(@plan).render
45
+ end
46
+ end
47
+
48
+ def rbs_files
49
+ Enumerator.new do |e|
50
+ e.yield "sig/#{@gem_name}.rbs", Views::Client::ModuleRbs.new(@plan).render
51
+ e.yield "sig/#{@gem_name}/types.rbs", Views::Client::TypesRbs.new(@plan).render
52
+ e.yield "sig/#{@gem_name}/schema.rbs", Views::Client::SchemaRbs.new(@plan).render
53
+ end
54
+ end
55
+
56
+ def should_skip_customizations?
57
+ Dir["#{destination_root}/**/*"].any? { |f| f.include?('/customizations.rb') }
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'generators/base'
4
+ require_relative 'generators/client'
5
+ require_relative 'generators/schema'
6
+
7
+ module Smithy
8
+ # Facilitates generation of artifacts.
9
+ module Generators
10
+ def self.generate(plan)
11
+ case plan.type
12
+ when :schema then Schema.new(plan).generate
13
+ when :client then Client.new(plan).generate
14
+ else
15
+ raise 'Not yet implemented'
16
+ end
17
+ end
18
+
19
+ def self.source(plan)
20
+ case plan.type
21
+ when :schema then Schema.new(plan).source
22
+ when :client then Client.new(plan).source
23
+ when :server then raise 'Will not be implemented'
24
+ else
25
+ raise 'Not yet implemented'
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,114 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Smithy
4
+ module Model
5
+ # @api private
6
+ class Flattener
7
+ def initialize(model)
8
+ @model = model
9
+ end
10
+
11
+ def shape(id)
12
+ shape = @model['shapes'][id]
13
+ return shape unless shape['mixins']
14
+
15
+ shape['mixins'].reverse_each do |mixin|
16
+ mixin_shape = shape(mixin['target'])
17
+ shape = deep_merge(mixin_shape, shape, exclude_traits(mixin_shape))
18
+ apply_traits(id, shape)
19
+ shape.delete('mixins')
20
+ end
21
+
22
+ shape
23
+ end
24
+
25
+ private
26
+
27
+ def exclude_traits(shape)
28
+ [
29
+ 'smithy.api#mixin',
30
+ *shape.fetch('traits', {}).fetch('smithy.api#mixin', {}).fetch('localTraits', [])
31
+ ]
32
+ end
33
+
34
+ def deep_merge(hash1, hash2, exclude_traits = [], context = nil)
35
+ hash1 = hash1.dup
36
+ if hash1['traits']
37
+ hash1['traits'] = hash1['traits'].except(*exclude_traits)
38
+ hash1.delete('traits') if hash1['traits'].empty?
39
+ end
40
+ deep_merge!(hash1, hash2, exclude_traits, context)
41
+ end
42
+
43
+ def deep_merge!(hash1, hash2, exclude_traits, context)
44
+ hash1.merge!(hash2) do |key, v1, v2|
45
+ if v1.is_a?(Hash) && v2.is_a?(Hash)
46
+ deep_merge(v1, v2, exclude_traits, key)
47
+ elsif v1.is_a?(Array) && v2.is_a?(Array) && context != 'traits'
48
+ # Merge arrays, but only if the key is not a trait
49
+ v1 + v2
50
+ else
51
+ v2
52
+ end
53
+ end
54
+ end
55
+
56
+ def apply_traits(id, shape)
57
+ case shape['type']
58
+ when 'structure'
59
+ structure(id, shape)
60
+ when 'union'
61
+ union(id, shape)
62
+ when 'list'
63
+ list(id, shape)
64
+ when 'map'
65
+ map_key(id, shape)
66
+ map_value(id, shape)
67
+ end
68
+ end
69
+
70
+ def structure(id, shape)
71
+ shape['members'].each do |member_name, member_shape|
72
+ member_id = "#{id}$#{member_name}"
73
+ next unless apply_shape_exists?(member_id)
74
+
75
+ apply_shape = shape(member_id)
76
+ member_keys = shape['members'][member_name].keys
77
+ shape['members'][member_name] = deep_merge(member_shape, apply_shape).slice(*member_keys)
78
+ end
79
+ end
80
+ alias union structure
81
+
82
+ def list(id, shape)
83
+ member_id = "#{id}$member"
84
+ return unless apply_shape_exists?(member_id)
85
+
86
+ apply_shape = shape(member_id)
87
+ member_keys = shape['member'].keys
88
+ shape['member'] = deep_merge(shape['member'], apply_shape).slice(*member_keys)
89
+ end
90
+
91
+ def map_key(id, shape)
92
+ key_id = "#{id}$key"
93
+ return unless apply_shape_exists?(key_id)
94
+
95
+ key_shape = shape(key_id)
96
+ key_keys = shape['key'].keys
97
+ shape['key'] = deep_merge(shape['key'], key_shape).slice(*key_keys)
98
+ end
99
+
100
+ def map_value(id, shape)
101
+ value_id = "#{id}$value"
102
+ return unless apply_shape_exists?(value_id)
103
+
104
+ value_shape = shape(value_id)
105
+ value_keys = shape['value'].keys
106
+ shape['value'] = deep_merge(shape['value'], value_shape).slice(*value_keys)
107
+ end
108
+
109
+ def apply_shape_exists?(id)
110
+ @model['shapes'][id] && @model['shapes'][id]['type'] == 'apply'
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Smithy
4
+ module Model
5
+ # @api private
6
+ class OperationParser
7
+ def initialize(model)
8
+ @model = model
9
+ @structure_parser = StructureParser.new(model)
10
+ end
11
+
12
+ def shapes_for(operation)
13
+ shapes = {}
14
+ _id, operation = operation.first
15
+ parse_input_output(operation['input'], shapes)
16
+ parse_input_output(operation['output'], shapes)
17
+ parse_errors(operation['errors'], shapes)
18
+ shapes
19
+ end
20
+
21
+ private
22
+
23
+ def parse_input_output(structure_shape, shapes)
24
+ target = structure_shape['target']
25
+ return if target == 'smithy.api#Unit'
26
+
27
+ shape = Model.shape(@model, target)
28
+ shapes[target] = shape
29
+ shapes.merge!(@structure_parser.shapes_for({ target => shape }))
30
+ end
31
+
32
+ def parse_errors(errors, shapes)
33
+ errors&.each do |error|
34
+ target = error['target']
35
+ shape = Model.shape(@model, target)
36
+ shapes[target] = shape
37
+ shapes.merge!(@structure_parser.shapes_for({ target => shape }))
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Smithy
4
+ module Model
5
+ # @api private
6
+ class RBS
7
+ class << self
8
+ # rubocop:disable Metrics/CyclomaticComplexity
9
+ def type(model, id, shape)
10
+ case shape['type']
11
+ when 'blob', 'string', 'enum' then 'String'
12
+ when 'boolean' then 'bool'
13
+ when 'byte', 'short', 'integer', 'bigInteger', 'long', 'intEnum' then 'Integer'
14
+ when 'bigDecimal' then 'BigDecimal'
15
+ when 'float', 'double' then 'Float'
16
+ when 'timestamp' then 'Time'
17
+ when 'document' then 'Smithy::Schema::document'
18
+ when 'list'
19
+ list_type(model, shape)
20
+ when 'map'
21
+ map_type(model, shape)
22
+ when 'structure', 'union'
23
+ structure_type(id)
24
+ else
25
+ 'untyped'
26
+ end
27
+ end
28
+ # rubocop:enable Metrics/CyclomaticComplexity
29
+
30
+ private
31
+
32
+ def structure_type(id)
33
+ if id == 'smithy.api#Unit'
34
+ 'Smithy::Schema::EmptyStructure'
35
+ else
36
+ "Types::#{Model::Shape.name(id)}"
37
+ end
38
+ end
39
+
40
+ def map_type(model, shape)
41
+ key_target = Model.shape(model, shape['key']['target'])
42
+ value_target = Model.shape(model, shape['value']['target'])
43
+ sparse = shape.fetch('traits', {}).key?('smithy.api#sparse')
44
+ key_type = type(model, shape['key']['target'], key_target)
45
+ value_type = type(model, shape['value']['target'], value_target)
46
+ "Hash[#{key_type}, #{value_type}#{'?' if sparse}]"
47
+ end
48
+
49
+ def list_type(model, shape)
50
+ member_target = Model.shape(model, shape['member']['target'])
51
+ sparse = shape.fetch('traits', {}).key?('smithy.api#sparse')
52
+ "Array[#{type(model, shape['member']['target'], member_target)}#{'?' if sparse}]"
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Smithy
4
+ module Model
5
+ # Finds sets of shapes for a service.
6
+ class ServiceIndex
7
+ # @param [Hash] model Model
8
+ def initialize(model)
9
+ @model = model
10
+ @service_parser = ServiceParser.new(model)
11
+ @operation_parser = OperationParser.new(model)
12
+ @structure_parser = StructureParser.new(model)
13
+ end
14
+
15
+ # @param [Hash] service Service shape
16
+ # @return [Hash<String, Hash>] The operations for the service.
17
+ def operations_for(service)
18
+ @operations_for ||= begin
19
+ shapes = @service_parser.operations_for(service)
20
+ shapes.sort_by { |k, _v| k }.to_h
21
+ end
22
+ end
23
+
24
+ # @param [Hash] service Service shape
25
+ # @return [Hash<String, Hash>] The shapes for the service.
26
+ def shapes_for(service)
27
+ @shapes_for ||= begin
28
+ shapes = {}
29
+ parse_errors(service, shapes)
30
+ operations_for(service).each do |id, operation|
31
+ shapes.merge!(@operation_parser.shapes_for({ id => operation }))
32
+ end
33
+ shapes.sort_by { |k, _v| k }.to_h
34
+ end
35
+ end
36
+
37
+ private
38
+
39
+ def parse_errors(service, shapes)
40
+ _, service = service.first
41
+
42
+ service['errors']&.each do |error|
43
+ target = error['target']
44
+ shape = Model.shape(@model, target)
45
+ shapes[target] = shape
46
+ shapes.merge!(@structure_parser.shapes_for({ target => shape }))
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Smithy
4
+ module Model
5
+ # @api private
6
+ class ServiceParser
7
+ RESOURCE_LIFECYCLE_KEYS = %w[create put read update delete list].freeze
8
+
9
+ def initialize(model)
10
+ @model = model
11
+ end
12
+
13
+ def operations_for(service)
14
+ operations = {}
15
+ _, service = service.first
16
+ parse_service_operations(service, operations)
17
+ parse_service_resources(service, operations)
18
+ operations
19
+ end
20
+
21
+ private
22
+
23
+ def parse_service_operations(service, operations)
24
+ service['operations']&.collect do |shape|
25
+ target = shape['target']
26
+ operations[target] = Model.shape(@model, target)
27
+ end
28
+ end
29
+
30
+ def parse_service_resources(service, operations)
31
+ service['resources']&.collect do |shape|
32
+ target = shape['target']
33
+ resource_shape = Model.shape(@model, target)
34
+ parse_resource(resource_shape, operations)
35
+ end
36
+ end
37
+
38
+ def parse_resource(resource, operations)
39
+ parse_lifecycles(resource, operations)
40
+ parse_resource_operations(resource, operations)
41
+ parse_resource_collection_operations(resource, operations)
42
+
43
+ resource['resources']&.collect do |shape|
44
+ target = shape['target']
45
+ resource_shape = Model.shape(@model, target)
46
+ parse_resource(resource_shape, operations)
47
+ end
48
+ end
49
+
50
+ def parse_lifecycles(resource, operations)
51
+ RESOURCE_LIFECYCLE_KEYS.each do |key|
52
+ next unless resource[key]
53
+
54
+ target = resource[key]['target']
55
+ operations[target] = Model.shape(@model, target)
56
+ end
57
+ end
58
+
59
+ def parse_resource_operations(resource, operations)
60
+ resource['operations']&.collect do |shape|
61
+ target = shape['target']
62
+ operations[target] = Model.shape(@model, target)
63
+ end
64
+ end
65
+
66
+ def parse_resource_collection_operations(resource, operations)
67
+ resource['collectionOperations']&.collect do |shape|
68
+ target = shape['target']
69
+ operations[target] = Model.shape(@model, target)
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end