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
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Smithy
|
4
|
+
module Model
|
5
|
+
# Represents a shape from the model.
|
6
|
+
# Shape IDs have the following syntax:
|
7
|
+
#
|
8
|
+
# smithy.example.foo#ExampleShapeName$memberName
|
9
|
+
# └─────────┬──────┘ └───────┬──────┘ └────┬───┘
|
10
|
+
# (Namespace) (Shape name) (Member name)
|
11
|
+
# └──────────────┬────────────┘
|
12
|
+
# (Relative shape ID)
|
13
|
+
# └──────────────────────┬───────────────────────┘
|
14
|
+
# (Absolute shape ID)
|
15
|
+
class Shape
|
16
|
+
# Optional shape namespace
|
17
|
+
# @param [String] id Shape ID
|
18
|
+
# @return [String, nil] Shape namespace
|
19
|
+
def self.namespace(id)
|
20
|
+
return nil unless id.include?('#')
|
21
|
+
|
22
|
+
id.split('#').first
|
23
|
+
end
|
24
|
+
|
25
|
+
# Relative shape ID
|
26
|
+
# @param [String] id Shape ID
|
27
|
+
# @return [String, nil] Relative shape ID
|
28
|
+
def self.relative_id(id)
|
29
|
+
id.split('#').last
|
30
|
+
end
|
31
|
+
|
32
|
+
# Shape name
|
33
|
+
# @param [String] id Shape ID
|
34
|
+
# @return [String, nil] Shape name
|
35
|
+
def self.name(id)
|
36
|
+
id.split('#').last&.split('$')&.first
|
37
|
+
end
|
38
|
+
|
39
|
+
# Optional member name
|
40
|
+
# @param [String] id Shape ID
|
41
|
+
# @return [String, nil] Member name
|
42
|
+
def self.member_name(id)
|
43
|
+
return nil unless id.include?('$')
|
44
|
+
|
45
|
+
id.split('$').last
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Smithy
|
4
|
+
module Model
|
5
|
+
# @api private
|
6
|
+
class StructureParser
|
7
|
+
def initialize(model)
|
8
|
+
@model = model
|
9
|
+
end
|
10
|
+
|
11
|
+
def shapes_for(structure)
|
12
|
+
shapes = {}
|
13
|
+
_id, structure = structure.first
|
14
|
+
structure['members']&.each_value { |member| parse_member(member, shapes) }
|
15
|
+
shapes
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def parse_member(member_shape, shapes)
|
21
|
+
target = member_shape['target']
|
22
|
+
return if shapes.key?(target)
|
23
|
+
return if Model::PRELUDE_SHAPES.key?(target)
|
24
|
+
|
25
|
+
shape = Model.shape(@model, target)
|
26
|
+
shapes[target] = shape
|
27
|
+
parse_shape(shape, shapes)
|
28
|
+
end
|
29
|
+
|
30
|
+
def parse_shape(shape, shapes)
|
31
|
+
case shape['type']
|
32
|
+
when 'list'
|
33
|
+
parse_member(shape['member'], shapes)
|
34
|
+
when 'map'
|
35
|
+
parse_member(shape['key'], shapes)
|
36
|
+
parse_member(shape['value'], shapes)
|
37
|
+
when 'structure', 'union', 'intEnum', 'enum'
|
38
|
+
shape['members']&.each_value { |member| parse_member(member, shapes) }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Smithy
|
4
|
+
module Model
|
5
|
+
# @api private
|
6
|
+
class YARD
|
7
|
+
class << self
|
8
|
+
def deprecated_docstrings(message, since)
|
9
|
+
lines = ['@deprecated']
|
10
|
+
lines << " #{escape(message)}" unless message.empty?
|
11
|
+
lines << " Since: #{escape(since)}" unless since.empty?
|
12
|
+
lines
|
13
|
+
end
|
14
|
+
|
15
|
+
def external_documentation_docstrings(hash)
|
16
|
+
hash.map { |key, value| "@see #{escape(value)} #{escape(key)}" }
|
17
|
+
end
|
18
|
+
|
19
|
+
def option_docstrings(service, model, id, shape, option, docstrings) # rubocop:disable Metrics/ParameterLists
|
20
|
+
lines = ["@option params [#{type(service, model, id, shape)}] :#{option}"]
|
21
|
+
docstrings.each do |docstring|
|
22
|
+
lines << " #{docstring}"
|
23
|
+
end
|
24
|
+
lines
|
25
|
+
end
|
26
|
+
|
27
|
+
def param_docstring(service, model, id, shape)
|
28
|
+
"@param [Hash, #{type(service, model, id, shape)}] params"
|
29
|
+
end
|
30
|
+
|
31
|
+
def recommended_docstrings(reason)
|
32
|
+
lines = ['@note']
|
33
|
+
lines << ' This shape is recommended'
|
34
|
+
lines << " Reason: #{escape(reason)}" unless reason.empty?
|
35
|
+
lines
|
36
|
+
end
|
37
|
+
|
38
|
+
def return_docstring(service, model, id, shape)
|
39
|
+
"@return [#{type(service, model, id, shape)}]"
|
40
|
+
end
|
41
|
+
|
42
|
+
def sensitive_docstring
|
43
|
+
'@note This shape contains sensitive data and should be treated as such.'
|
44
|
+
end
|
45
|
+
|
46
|
+
def since_docstring(since)
|
47
|
+
"@since #{escape(since)}"
|
48
|
+
end
|
49
|
+
|
50
|
+
def title_docstring(title)
|
51
|
+
"@title #{escape(title)}"
|
52
|
+
end
|
53
|
+
|
54
|
+
def unstable_docstring
|
55
|
+
'@note This shape is unstable and may change in future releases.'
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def type(service, model, id, shape) # rubocop:disable Metrics/CyclomaticComplexity
|
61
|
+
case shape['type']
|
62
|
+
when 'blob', 'string', 'enum' then 'String'
|
63
|
+
when 'boolean' then 'Boolean'
|
64
|
+
when 'byte', 'short', 'integer', 'long', 'intEnum' then 'Integer'
|
65
|
+
when 'float', 'double' then 'Float'
|
66
|
+
when 'timestamp' then 'Time'
|
67
|
+
when 'document' then 'JSON'
|
68
|
+
when 'list' then list(service, model, shape)
|
69
|
+
when 'map' then map(service, model, shape)
|
70
|
+
when 'structure', 'union' then structure(service, id)
|
71
|
+
else 'Object'
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def structure(service, id)
|
76
|
+
return 'Smithy::Schema::EmptyStructure' if id == 'smithy.api#Unit'
|
77
|
+
|
78
|
+
"Types::#{(service.dig('rename', id) || Model::Shape.name(id)).camelize}"
|
79
|
+
end
|
80
|
+
|
81
|
+
def map(service, model, shape)
|
82
|
+
key_target = Model.shape(model, shape['key']['target'])
|
83
|
+
value_target = Model.shape(model, shape['value']['target'])
|
84
|
+
key_type = type(service, model, shape['key']['target'], key_target)
|
85
|
+
value_type = type(service, model, shape['value']['target'], value_target)
|
86
|
+
"Hash<#{key_type}, #{value_type}>"
|
87
|
+
end
|
88
|
+
|
89
|
+
def list(service, model, shape)
|
90
|
+
member_target = Model.shape(model, shape['member']['target'])
|
91
|
+
"Array<#{type(service, model, shape['member']['target'], member_target)}>"
|
92
|
+
end
|
93
|
+
|
94
|
+
def escape(string)
|
95
|
+
string.split("\n").join(' ')
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
data/lib/smithy/model.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'model/flattener'
|
4
|
+
require_relative 'model/operation_parser'
|
5
|
+
require_relative 'model/rbs'
|
6
|
+
require_relative 'model/service_index'
|
7
|
+
require_relative 'model/service_parser'
|
8
|
+
require_relative 'model/shape'
|
9
|
+
require_relative 'model/structure_parser'
|
10
|
+
require_relative 'model/yard'
|
11
|
+
|
12
|
+
module Smithy
|
13
|
+
# A module that parses the Smithy JSON model.
|
14
|
+
module Model
|
15
|
+
# Generated with smithy-cli 1.53.0
|
16
|
+
# @api private
|
17
|
+
PRELUDE_SHAPES = {
|
18
|
+
'smithy.api#BigInteger' => { 'type' => 'bigInteger' },
|
19
|
+
'smithy.api#BigDecimal' => { 'type' => 'bigDecimal' },
|
20
|
+
'smithy.api#Blob' => { 'type' => 'blob' },
|
21
|
+
'smithy.api#Boolean' => { 'type' => 'boolean' },
|
22
|
+
'smithy.api#Byte' => { 'type' => 'byte' },
|
23
|
+
'smithy.api#Document' => { 'type' => 'document' },
|
24
|
+
'smithy.api#Double' => { 'type' => 'double' },
|
25
|
+
'smithy.api#Float' => { 'type' => 'float' },
|
26
|
+
'smithy.api#Integer' => { 'type' => 'integer' },
|
27
|
+
'smithy.api#Long' => { 'type' => 'long' },
|
28
|
+
'smithy.api#PrimitiveBoolean' => { 'type' => 'boolean', 'traits' => { 'smithy.api#default' => false } },
|
29
|
+
'smithy.api#PrimitiveByte' => { 'type' => 'byte', 'traits' => { 'smithy.api#default' => 0 } },
|
30
|
+
'smithy.api#PrimitiveDouble' => { 'type' => 'double', 'traits' => { 'smithy.api#default' => 0 } },
|
31
|
+
'smithy.api#PrimitiveFloat' => { 'type' => 'float', 'traits' => { 'smithy.api#default' => 0 } },
|
32
|
+
'smithy.api#PrimitiveInteger' => { 'type' => 'integer', 'traits' => { 'smithy.api#default' => 0 } },
|
33
|
+
'smithy.api#PrimitiveLong' => { 'type' => 'long', 'traits' => { 'smithy.api#default' => 0 } },
|
34
|
+
'smithy.api#PrimitiveShort' => { 'type' => 'short', 'traits' => { 'smithy.api#default' => 0 } },
|
35
|
+
'smithy.api#Short' => { 'type' => 'short' },
|
36
|
+
'smithy.api#String' => { 'type' => 'string' },
|
37
|
+
'smithy.api#Timestamp' => { 'type' => 'timestamp' },
|
38
|
+
'smithy.api#Unit' => { 'type' => 'structure', 'members' => {}, 'traits' => { 'smithy.api#unitType' => {} } }
|
39
|
+
}.freeze
|
40
|
+
|
41
|
+
# @param [Hash] model Model
|
42
|
+
# @param [String] id Shape ID
|
43
|
+
# @return [Hash]
|
44
|
+
def self.shape(model, id)
|
45
|
+
if model['shapes'].key?(id)
|
46
|
+
Flattener.new(model).shape(id)
|
47
|
+
elsif PRELUDE_SHAPES.key?(id)
|
48
|
+
PRELUDE_SHAPES[id]
|
49
|
+
else
|
50
|
+
raise ArgumentError, "Shape not found: #{id}"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
data/lib/smithy/plan.rb
CHANGED
@@ -1,10 +1,86 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Smithy
|
4
|
+
# The Plan class is a simple data structure that holds the model, type, and options for a generator.
|
4
5
|
class Plan
|
5
|
-
|
6
|
-
|
7
|
-
|
6
|
+
# @param [Hash] model The API model as a JSON hash.
|
7
|
+
# @param [Symbol] type The type of code to generate, either :client, :server, or :schema.
|
8
|
+
# @param [Hash] options
|
9
|
+
# @option options [String] :name The name of the service to generate code for.
|
10
|
+
# @option options [String] :module_name The module name for clients and schemas.
|
11
|
+
# Defaults to the name of the service.
|
12
|
+
# @option options [String] :gem_name The gem name for clients and schemas.
|
13
|
+
# Defaults to a gem name derived from the module name, suffixed with '-schema' if type is schema.
|
14
|
+
# @option options [String] :gem_version The version of the gem to generate.
|
15
|
+
# @option options [String] :destination_root The destination directory for the generated code.
|
16
|
+
# If not provided, source code will be generated to STDOUT.
|
17
|
+
# @option options [Boolean] :quiet Whether to suppress output.
|
18
|
+
def initialize(model, type, options = {})
|
19
|
+
@model = model
|
20
|
+
@type = type
|
21
|
+
@service = find_service(model['shapes'])
|
22
|
+
|
23
|
+
@name = options.fetch(:name, default_name(@service))
|
24
|
+
@module_name = options.fetch(:module_name, @name)
|
25
|
+
@gem_name = options.fetch(:gem_name, default_gem_name(@module_name, @type))
|
26
|
+
@gem_version = options.fetch(:gem_version)
|
27
|
+
|
28
|
+
@destination_root = options.fetch(:destination_root, nil)
|
29
|
+
@quiet = options.fetch(:quiet, false)
|
30
|
+
|
31
|
+
Welds.load!(self)
|
32
|
+
@welds = Welds.for(@service)
|
33
|
+
end
|
34
|
+
|
35
|
+
# @return [Hash] The API model as a JSON hash.
|
36
|
+
attr_reader :model
|
37
|
+
|
38
|
+
# @return [Symbol] The type of code to generate.
|
39
|
+
attr_reader :type
|
40
|
+
|
41
|
+
# @return [Hash<String, Hash>] The service shape for the shapes.
|
42
|
+
attr_reader :service
|
43
|
+
|
44
|
+
# @return [String] The name of the service.
|
45
|
+
attr_reader :name
|
46
|
+
|
47
|
+
# @return [String] The module name for clients and schemas.
|
48
|
+
attr_reader :module_name
|
49
|
+
|
50
|
+
# @return [String] The gem name for clients and schemas.
|
51
|
+
attr_reader :gem_name
|
52
|
+
|
53
|
+
# @return [String] The version of the gem to generate.
|
54
|
+
attr_reader :gem_version
|
55
|
+
|
56
|
+
# @return [String] The destination directory for the generated code.
|
57
|
+
attr_reader :destination_root
|
58
|
+
|
59
|
+
# @return [Boolean] Whether to suppress output.
|
60
|
+
attr_reader :quiet
|
61
|
+
|
62
|
+
# @return [Array<Weld>] The welds that apply to this plan.
|
63
|
+
attr_reader :welds
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def find_service(shapes)
|
68
|
+
service = shapes.select { |_, shape| shape['type'] == 'service' }
|
69
|
+
raise 'Multiple service shapes found' if service.size > 1
|
70
|
+
raise 'No service shape found' if service.empty?
|
71
|
+
|
72
|
+
service
|
73
|
+
end
|
74
|
+
|
75
|
+
def default_name(service)
|
76
|
+
Model::Shape.name(service.keys.first)
|
77
|
+
end
|
78
|
+
|
79
|
+
def default_gem_name(module_name, type)
|
80
|
+
gem_name = module_name.split('::').map do |part|
|
81
|
+
part.gsub(/([a-z\d])([A-Z])/, '\1_\2').downcase
|
82
|
+
end.join('-')
|
83
|
+
type == :schema ? "#{gem_name.downcase}-schema" : gem_name.downcase
|
8
84
|
end
|
9
85
|
end
|
10
86
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# This is generated code!
|
4
|
+
|
5
|
+
module <%= module_name %>
|
6
|
+
# Auth parameters used to resolve authentication per request.
|
7
|
+
<% parameters.each do |param| -%>
|
8
|
+
# @!attribute <%= param.name %>
|
9
|
+
<% param.docstrings.each do |docstring| -%>
|
10
|
+
# <%= docstring %>
|
11
|
+
<% end -%>
|
12
|
+
#
|
13
|
+
# @return [<%= param.documentation_type %>]
|
14
|
+
#
|
15
|
+
<% end -%>
|
16
|
+
AuthParameters = Struct.new(
|
17
|
+
<% parameters.each do |param| -%>
|
18
|
+
:<%= param.name %>,
|
19
|
+
<% end -%>
|
20
|
+
keyword_init: true
|
21
|
+
) do
|
22
|
+
|
23
|
+
# @api private
|
24
|
+
def self.create(context)
|
25
|
+
# TODO: support more properties
|
26
|
+
new(operation_name: context.operation_name)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module <%= module_name %>
|
2
|
+
class AuthParameters
|
3
|
+
def initialize: (
|
4
|
+
<% parameters.each do |param| -%>
|
5
|
+
<%= '?' unless param.name == :operation_name %><%= param.name %>: <%= param.rbs_type %>,
|
6
|
+
<% end -%>
|
7
|
+
) -> void
|
8
|
+
| (?Hash[Symbol, untyped]) -> void
|
9
|
+
|
10
|
+
<% parameters.each do |param| -%>
|
11
|
+
attr_accessor <%= param.name %>: <%= param.rbs_type %><%= '?' unless param.name == :operation_name %>
|
12
|
+
<% end -%>
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# This is generated code!
|
4
|
+
|
5
|
+
module <%= module_name %>
|
6
|
+
module Plugins
|
7
|
+
# @api private
|
8
|
+
class Auth < Smithy::Client::Plugin
|
9
|
+
option(
|
10
|
+
:auth_resolver,
|
11
|
+
doc_type: '<%= module_name %>::AuthResolver',
|
12
|
+
docstring: <<~DOCS) do |config|
|
13
|
+
The auth resolver used to resolve authentication. Any object that responds to `#resolve(parameters)`.
|
14
|
+
DOCS
|
15
|
+
AuthResolver.new
|
16
|
+
end
|
17
|
+
|
18
|
+
option(
|
19
|
+
:auth_schemes,
|
20
|
+
doc_type: Hash,
|
21
|
+
rbs_type: 'Hash[String, Smithy::Client::AuthScheme]',
|
22
|
+
docstring: <<~DOCS) do |config|
|
23
|
+
The auth schemes used to resolve authentication. The key is the scheme name as a String,
|
24
|
+
and the value is an initialized auth scheme class.
|
25
|
+
DOCS
|
26
|
+
{
|
27
|
+
<% auth_schemes.each do |k, v| -%>
|
28
|
+
'<%= k %>' => config.<%= v %>,
|
29
|
+
<% end -%>
|
30
|
+
}
|
31
|
+
end
|
32
|
+
|
33
|
+
# @api private
|
34
|
+
class Handler < Smithy::Client::Handler
|
35
|
+
def call(context)
|
36
|
+
# TODO: apply endpoint auth properties if present
|
37
|
+
auth_params = AuthParameters.create(context)
|
38
|
+
auth_options = context.config.auth_resolver.resolve(auth_params)
|
39
|
+
context[:auth] = resolve_auth(context, auth_options)
|
40
|
+
@handler.call(context)
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def resolve_auth(context, auth_options)
|
46
|
+
failures = []
|
47
|
+
|
48
|
+
raise 'No auth options were resolved' if auth_options.empty?
|
49
|
+
|
50
|
+
identity_providers = {
|
51
|
+
<% identity_providers.each do |k, v| -%>
|
52
|
+
<%= k %> => context.config.<%= v %>,
|
53
|
+
<% end -%>
|
54
|
+
}
|
55
|
+
|
56
|
+
auth_options.each do |auth_option|
|
57
|
+
auth_scheme = context.config.auth_schemes[auth_option.scheme_id]
|
58
|
+
resolved_auth = try_load_auth_scheme(
|
59
|
+
auth_option,
|
60
|
+
auth_scheme,
|
61
|
+
identity_providers,
|
62
|
+
failures
|
63
|
+
)
|
64
|
+
|
65
|
+
return resolved_auth if resolved_auth
|
66
|
+
end
|
67
|
+
|
68
|
+
raise failures.join("\n")
|
69
|
+
end
|
70
|
+
|
71
|
+
def try_load_auth_scheme(auth_option, auth_scheme, identity_providers, failures)
|
72
|
+
scheme_id = auth_option.scheme_id
|
73
|
+
unless auth_scheme
|
74
|
+
failures << "Auth scheme #{scheme_id} was not enabled " \
|
75
|
+
'for this request'
|
76
|
+
return
|
77
|
+
end
|
78
|
+
|
79
|
+
identity_provider = auth_scheme.identity_provider(identity_providers)
|
80
|
+
unless identity_provider
|
81
|
+
failures << "Auth scheme #{scheme_id} did not have an " \
|
82
|
+
'identity resolver configured'
|
83
|
+
return
|
84
|
+
end
|
85
|
+
|
86
|
+
identity_properties = auth_option.identity_properties
|
87
|
+
identity = identity_provider.identity(identity_properties)
|
88
|
+
|
89
|
+
ResolvedAuth.new(
|
90
|
+
scheme_id: scheme_id,
|
91
|
+
identity: identity,
|
92
|
+
identity_properties: auth_option.identity_properties,
|
93
|
+
signer: auth_scheme.signer,
|
94
|
+
signer_properties: auth_option.signer_properties
|
95
|
+
)
|
96
|
+
end
|
97
|
+
|
98
|
+
# @api private
|
99
|
+
class ResolvedAuth
|
100
|
+
def initialize(options = {})
|
101
|
+
@scheme_id = options[:scheme_id]
|
102
|
+
@signer = options[:signer]
|
103
|
+
@signer_properties = options[:signer_properties]
|
104
|
+
@identity = options[:identity]
|
105
|
+
@identity_properties = options[:identity_properties]
|
106
|
+
end
|
107
|
+
|
108
|
+
attr_accessor :scheme_id, :signer, :signer_properties, :identity, :identity_properties
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
handler(Handler, step: :sign, priority: 70)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# This is generated code!
|
4
|
+
|
5
|
+
module <%= module_name %>
|
6
|
+
# Resolves the auth scheme from {AuthParameters}.
|
7
|
+
class AuthResolver
|
8
|
+
# @param [AuthParameters] parameters
|
9
|
+
# @return [Smithy::Client::AuthOption]
|
10
|
+
def resolve(parameters)
|
11
|
+
<% auth_rules_code.each do |line| -%>
|
12
|
+
<%= line %>
|
13
|
+
<% end -%>
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,142 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# This is generated code!
|
4
|
+
|
5
|
+
<%require_plugins.each do |require| -%>
|
6
|
+
<%= require %>
|
7
|
+
<% end -%>
|
8
|
+
|
9
|
+
module <%= module_name %>
|
10
|
+
# An API client for <%= module_name %>.
|
11
|
+
# See {#initialize} for a full list of supported configuration options.
|
12
|
+
class Client < Smithy::Client::Base
|
13
|
+
include Smithy::Client::Stubs
|
14
|
+
|
15
|
+
self.service = Schema::<%= service_name %>
|
16
|
+
|
17
|
+
<% add_plugins.each do |plugin_class| -%>
|
18
|
+
add_plugin(<%= plugin_class %>)
|
19
|
+
<% end -%>
|
20
|
+
|
21
|
+
# @param options [Hash] Client options
|
22
|
+
<% docstrings.each do |docstring| -%>
|
23
|
+
# <%= docstring %>
|
24
|
+
<% end -%>
|
25
|
+
def initialize(*options)
|
26
|
+
super
|
27
|
+
end
|
28
|
+
|
29
|
+
<% operations.each do |operation| -%>
|
30
|
+
<% operation.docstrings.each do |docstring| -%>
|
31
|
+
# <%= docstring %>
|
32
|
+
<% end -%>
|
33
|
+
def <%= operation.method_name %>(params = {}, options = {})
|
34
|
+
request = build_request(:<%= operation.method_name %>, params)
|
35
|
+
request.send_request(options)
|
36
|
+
end
|
37
|
+
|
38
|
+
<% end -%>
|
39
|
+
# Polls an API operation until a resource enters a desired state.
|
40
|
+
#
|
41
|
+
# ## Basic Usage
|
42
|
+
#
|
43
|
+
# A waiter will call an API operation until:
|
44
|
+
#
|
45
|
+
# * It is successful
|
46
|
+
# * It enters a terminal state
|
47
|
+
# * It reaches the maximum wait time allowed
|
48
|
+
#
|
49
|
+
# In between attempts, the waiter will sleep.
|
50
|
+
#
|
51
|
+
# # polls in a loop, sleeping between attempts
|
52
|
+
# client.wait_until(waiter_name, params, options)
|
53
|
+
#
|
54
|
+
# ## Configuration
|
55
|
+
#
|
56
|
+
# You must configure the maximum amount of time in seconds a
|
57
|
+
# waiter should wait for. You may also configure the minimum
|
58
|
+
# and maximum amount of time in seconds to delay between
|
59
|
+
# retries. You can pass these configuration as the final
|
60
|
+
# arguments hash.
|
61
|
+
#
|
62
|
+
# weather = Weather::Client.new
|
63
|
+
#
|
64
|
+
# # poll for a maximum of 25 seconds
|
65
|
+
# weather.wait_until(:forecast_exists, { forecast_id: '1' }, {
|
66
|
+
# max_wait_time: 25,
|
67
|
+
# min_delay: 2,
|
68
|
+
# max_delay: 10
|
69
|
+
# })
|
70
|
+
#
|
71
|
+
# ## Handling Errors
|
72
|
+
#
|
73
|
+
# When a waiter is unsuccessful, it will raise an error.
|
74
|
+
# All the failure errors extend from
|
75
|
+
# {Smithy::Client::Waiters::WaiterFailed}.
|
76
|
+
#
|
77
|
+
# weather = Weather::Client.new
|
78
|
+
# begin
|
79
|
+
# weather.wait_until(:forecast_exists, { forecast_id: '1' }, max_wait_time: 60)
|
80
|
+
# rescue Smithy::Client::Waiters::WaiterFailed
|
81
|
+
# # resource did not enter the desired state in time
|
82
|
+
# end
|
83
|
+
#
|
84
|
+
# @param [Symbol] waiter_name
|
85
|
+
# @param [Hash] params ({})
|
86
|
+
# @param [Hash] options ({})
|
87
|
+
# @option options [Integer] :max_wait_time
|
88
|
+
# @option options [Integer] :min_delay
|
89
|
+
# @option options [Integer] :max_delay
|
90
|
+
# @return [nil] Returns `nil` if the waiter was successful.
|
91
|
+
# @raise [FailureStateError] Raised when the waiter terminates
|
92
|
+
# because the waiter has entered a state that it will not transition
|
93
|
+
# out of, preventing success.
|
94
|
+
# @raise [MaxWaitTimeExceededError] Raised when the configured
|
95
|
+
# maximum wait time is reached and the waiter is not yet successful.
|
96
|
+
# @raise [UnexpectedError] Raised when an error that is not
|
97
|
+
# expected is encountered while polling for a resource.
|
98
|
+
# @raise [NoSuchWaiterError] Raised when you request to wait
|
99
|
+
# for an unknown state.
|
100
|
+
def wait_until(waiter_name, params = {}, options = {})
|
101
|
+
waiter(waiter_name, options).wait(params)
|
102
|
+
end
|
103
|
+
|
104
|
+
# @api private
|
105
|
+
def build_request(operation_name, params)
|
106
|
+
handlers = @handlers.for(operation_name)
|
107
|
+
context = Smithy::Client::HandlerContext.new(
|
108
|
+
operation_name: operation_name,
|
109
|
+
operation: config.service.operation(operation_name),
|
110
|
+
client: self,
|
111
|
+
config: config,
|
112
|
+
params: params
|
113
|
+
)
|
114
|
+
context[:gem_name] = '<%= gem_name %>'
|
115
|
+
context[:gem_version] = '<%= gem_version %>'
|
116
|
+
Smithy::Client::Request.new(handlers: handlers, context: context)
|
117
|
+
end
|
118
|
+
|
119
|
+
private
|
120
|
+
|
121
|
+
def waiters
|
122
|
+
<% waiters.each do |line| -%>
|
123
|
+
<%= line %>
|
124
|
+
<% end -%>
|
125
|
+
end
|
126
|
+
|
127
|
+
class << self
|
128
|
+
# @api private
|
129
|
+
attr_reader :identifier
|
130
|
+
|
131
|
+
# @api private
|
132
|
+
def protocols
|
133
|
+
<%= protocols %>
|
134
|
+
end
|
135
|
+
|
136
|
+
# @api private
|
137
|
+
def errors_module
|
138
|
+
Errors
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|