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.
- checksums.yaml +4 -4
- data/VERSION +1 -1
- data/bin/smithy-ruby +1 -4
- data/lib/smithy/command.rb +68 -0
- data/lib/smithy/generators/base.rb +19 -0
- data/lib/smithy/generators/client.rb +106 -0
- data/lib/smithy/generators/schema.rb +61 -0
- data/lib/smithy/generators.rb +29 -0
- data/lib/smithy/model/flattener.rb +114 -0
- data/lib/smithy/model/operation_parser.rb +42 -0
- data/lib/smithy/model/rbs.rb +57 -0
- data/lib/smithy/model/service_index.rb +51 -0
- data/lib/smithy/model/service_parser.rb +74 -0
- data/lib/smithy/model/shape.rb +49 -0
- data/lib/smithy/model/structure_parser.rb +43 -0
- data/lib/smithy/model/yard.rb +100 -0
- data/lib/smithy/model.rb +54 -0
- data/lib/smithy/plan.rb +79 -3
- data/lib/smithy/templates/client/auth_parameters.erb +29 -0
- data/lib/smithy/templates/client/auth_parameters_rbs.erb +14 -0
- data/lib/smithy/templates/client/auth_plugin.erb +115 -0
- data/lib/smithy/templates/client/auth_resolver.erb +16 -0
- data/lib/smithy/templates/client/auth_resolver_rbs.erb +5 -0
- data/lib/smithy/templates/client/client.erb +142 -0
- data/lib/smithy/templates/client/client_rbs.erb +29 -0
- data/lib/smithy/templates/client/customizations.erb +3 -0
- data/lib/smithy/templates/client/endpoint_parameters.erb +65 -0
- data/lib/smithy/templates/client/endpoint_parameters_rbs.erb +13 -0
- data/lib/smithy/templates/client/endpoint_plugin.erb +58 -0
- data/lib/smithy/templates/client/endpoint_provider.erb +15 -0
- data/lib/smithy/templates/client/endpoint_provider_rbs.erb +5 -0
- data/lib/smithy/templates/client/endpoint_provider_spec.erb +70 -0
- data/lib/smithy/templates/client/errors.erb +69 -0
- data/lib/smithy/templates/client/errors_rbs.erb +17 -0
- data/lib/smithy/templates/client/gemspec.erb +17 -0
- data/lib/smithy/templates/client/module.erb +22 -0
- data/lib/smithy/templates/client/module_rbs.erb +7 -0
- data/lib/smithy/templates/client/paginators.erb +33 -0
- data/lib/smithy/templates/client/protocol_spec.erb +144 -0
- data/lib/smithy/templates/client/rubocop_yml.erb +33 -0
- data/lib/smithy/templates/client/schema.erb +76 -0
- data/lib/smithy/templates/client/schema_rbs.erb +13 -0
- data/lib/smithy/templates/client/spec_helper.erb +10 -0
- data/lib/smithy/templates/client/types.erb +64 -0
- data/lib/smithy/templates/client/types_rbs.erb +47 -0
- data/lib/smithy/templates/client/waiters.erb +42 -0
- data/lib/smithy/util/hash_formatter.rb +124 -0
- data/lib/smithy/util/underscore.rb +18 -0
- data/lib/smithy/util.rb +9 -0
- data/lib/smithy/views/client/auth_parameter.rb +29 -0
- data/lib/smithy/views/client/auth_parameters.rb +23 -0
- data/lib/smithy/views/client/auth_parameters_rbs.rb +23 -0
- data/lib/smithy/views/client/auth_plugin.rb +35 -0
- data/lib/smithy/views/client/auth_resolver.rb +125 -0
- data/lib/smithy/views/client/auth_resolver_rbs.rb +19 -0
- data/lib/smithy/views/client/client.rb +208 -0
- data/lib/smithy/views/client/client_rbs.rb +231 -0
- data/lib/smithy/views/client/customizations.rb +10 -0
- data/lib/smithy/views/client/endpoint_parameter.rb +156 -0
- data/lib/smithy/views/client/endpoint_parameters.rb +43 -0
- data/lib/smithy/views/client/endpoint_parameters_rbs.rb +28 -0
- data/lib/smithy/views/client/endpoint_plugin.rb +27 -0
- data/lib/smithy/views/client/endpoint_provider.rb +241 -0
- data/lib/smithy/views/client/endpoint_provider_rbs.rb +19 -0
- data/lib/smithy/views/client/endpoint_provider_spec.rb +137 -0
- data/lib/smithy/views/client/errors.rb +88 -0
- data/lib/smithy/views/client/errors_rbs.rb +12 -0
- data/lib/smithy/views/client/gemspec.rb +36 -0
- data/lib/smithy/views/client/module.rb +107 -0
- data/lib/smithy/views/client/module_rbs.rb +20 -0
- data/lib/smithy/views/client/operation_examples.rb +157 -0
- data/lib/smithy/views/client/paginators.rb +108 -0
- data/lib/smithy/views/client/plugin.rb +29 -0
- data/lib/smithy/views/client/plugin_list.rb +57 -0
- data/lib/smithy/views/client/protocol_spec.rb +254 -0
- data/lib/smithy/views/client/request_response_example.rb +179 -0
- data/lib/smithy/views/client/rubocop_yml.rb +19 -0
- data/lib/smithy/views/client/schema.rb +356 -0
- data/lib/smithy/views/client/schema_rbs.rb +84 -0
- data/lib/smithy/views/client/shape_to_hash.rb +99 -0
- data/lib/smithy/views/client/spec_helper.rb +19 -0
- data/lib/smithy/views/client/types.rb +293 -0
- data/lib/smithy/views/client/types_rbs.rb +67 -0
- data/lib/smithy/views/client/waiters.rb +82 -0
- data/lib/smithy/views/client.rb +47 -0
- data/lib/smithy/views/view.rb +30 -0
- data/lib/smithy/views.rb +9 -0
- data/lib/smithy/weld.rb +109 -0
- data/lib/smithy/welds/auth/anonymous_auth.rb +31 -0
- data/lib/smithy/welds/auth/http_api_key_auth.rb +34 -0
- data/lib/smithy/welds/auth/http_basic_auth.rb +34 -0
- data/lib/smithy/welds/auth/http_bearer_auth.rb +34 -0
- data/lib/smithy/welds/auth/http_digest_auth.rb +34 -0
- data/lib/smithy/welds/plugins.rb +54 -0
- data/lib/smithy/welds/rpc_v2_cbor.rb +20 -0
- data/lib/smithy/welds/rubocop.rb +33 -0
- data/lib/smithy/welds/transforms/default_endpoint_rules.json +35 -0
- data/lib/smithy/welds/transforms/default_endpoint_tests.json +24 -0
- data/lib/smithy/welds/transforms/endpoints.rb +68 -0
- data/lib/smithy/welds/transforms/synthetic_input_output.rb +60 -0
- data/lib/smithy/welds.rb +29 -0
- data/lib/smithy.rb +33 -2
- metadata +144 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a3d6e2e6c2474e08572ffac8b6d9e014d61433a94b27329c04c58ad964f1f647
|
4
|
+
data.tar.gz: 50392ce54b2239d61b8a5c1189b9e55bdd8e3c9558f163f1d005b5b91ff85867
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d4b2bc367e168ed6e0841bb6efe7b5b949910efcf36778d4a95afa127935fb758929128cc9d93feae7402a98185e629e16461cbd7f38a3d2910169076b740494
|
7
|
+
data.tar.gz: 32bd9986f0dc5a9c9c52414d48c5dcd2f0213d8115d475a4f50dbe6ac375a47a0ce30b5a5a39e815cb716ca3ff2c72c4e4f0ff59181b7699f54c1edc19b6dca0
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.0.0.
|
1
|
+
2.0.0.pre1
|
data/bin/smithy-ruby
CHANGED
@@ -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
|