smithy-client 1.0.0.pre0 → 1.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/CHANGELOG.md +2 -0
- data/VERSION +1 -1
- data/lib/smithy-client/anonymous_provider.rb +12 -0
- data/lib/smithy-client/auth_option.rb +23 -0
- data/lib/smithy-client/auth_scheme.rb +25 -0
- data/lib/smithy-client/auth_schemes/anonymous.rb +18 -0
- data/lib/smithy-client/auth_schemes/http_api_key.rb +18 -0
- data/lib/smithy-client/auth_schemes/http_basic.rb +18 -0
- data/lib/smithy-client/auth_schemes/http_bearer.rb +18 -0
- data/lib/smithy-client/auth_schemes/http_digest.rb +18 -0
- data/lib/smithy-client/base.rb +200 -0
- data/lib/smithy-client/block_io.rb +36 -0
- data/lib/smithy-client/configuration.rb +222 -0
- data/lib/smithy-client/default_params.rb +91 -0
- data/lib/smithy-client/dynamic_errors.rb +82 -0
- data/lib/smithy-client/endpoint_rules.rb +186 -0
- data/lib/smithy-client/handler.rb +29 -0
- data/lib/smithy-client/handler_builder.rb +33 -0
- data/lib/smithy-client/handler_context.rb +67 -0
- data/lib/smithy-client/handler_list.rb +197 -0
- data/lib/smithy-client/handler_list_entry.rb +102 -0
- data/lib/smithy-client/http/error_inspector.rb +87 -0
- data/lib/smithy-client/http/headers.rb +122 -0
- data/lib/smithy-client/http/request.rb +57 -0
- data/lib/smithy-client/http/response.rb +178 -0
- data/lib/smithy-client/http_api_key_provider.rb +18 -0
- data/lib/smithy-client/http_bearer_provider.rb +18 -0
- data/lib/smithy-client/http_login_provider.rb +19 -0
- data/lib/smithy-client/identities/anonymous.rb +10 -0
- data/lib/smithy-client/identities/http_api_key.rb +18 -0
- data/lib/smithy-client/identities/http_bearer.rb +18 -0
- data/lib/smithy-client/identities/http_login.rb +22 -0
- data/lib/smithy-client/identity.rb +15 -0
- data/lib/smithy-client/log_formatter.rb +215 -0
- data/lib/smithy-client/log_param_filter.rb +88 -0
- data/lib/smithy-client/log_param_formatter.rb +65 -0
- data/lib/smithy-client/managed_file.rb +14 -0
- data/lib/smithy-client/net_http/connection_pool.rb +297 -0
- data/lib/smithy-client/net_http/handler.rb +160 -0
- data/lib/smithy-client/net_http/patches.rb +28 -0
- data/lib/smithy-client/networking_error.rb +16 -0
- data/lib/smithy-client/pageable_response.rb +138 -0
- data/lib/smithy-client/param_converter.rb +243 -0
- data/lib/smithy-client/param_validator.rb +213 -0
- data/lib/smithy-client/plugin.rb +144 -0
- data/lib/smithy-client/plugin_list.rb +141 -0
- data/lib/smithy-client/plugins/anonymous_auth.rb +23 -0
- data/lib/smithy-client/plugins/checksum_required.rb +51 -0
- data/lib/smithy-client/plugins/content_length.rb +26 -0
- data/lib/smithy-client/plugins/default_params.rb +22 -0
- data/lib/smithy-client/plugins/host_prefix.rb +69 -0
- data/lib/smithy-client/plugins/http_api_key_auth.rb +37 -0
- data/lib/smithy-client/plugins/http_basic_auth.rb +47 -0
- data/lib/smithy-client/plugins/http_bearer_auth.rb +37 -0
- data/lib/smithy-client/plugins/http_digest_auth.rb +60 -0
- data/lib/smithy-client/plugins/idempotency_token.rb +34 -0
- data/lib/smithy-client/plugins/logging.rb +56 -0
- data/lib/smithy-client/plugins/net_http.rb +163 -0
- data/lib/smithy-client/plugins/pageable_response.rb +37 -0
- data/lib/smithy-client/plugins/param_converter.rb +32 -0
- data/lib/smithy-client/plugins/param_validator.rb +30 -0
- data/lib/smithy-client/plugins/protocol.rb +66 -0
- data/lib/smithy-client/plugins/raise_response_errors.rb +33 -0
- data/lib/smithy-client/plugins/request_compression.rb +200 -0
- data/lib/smithy-client/plugins/response_target.rb +71 -0
- data/lib/smithy-client/plugins/retry_errors.rb +125 -0
- data/lib/smithy-client/plugins/sign_requests.rb +24 -0
- data/lib/smithy-client/plugins/stub_responses.rb +102 -0
- data/lib/smithy-client/protocol_spec_matcher.rb +60 -0
- data/lib/smithy-client/refreshing_identity_provider.rb +65 -0
- data/lib/smithy-client/request.rb +76 -0
- data/lib/smithy-client/response.rb +48 -0
- data/lib/smithy-client/retry/adaptive.rb +66 -0
- data/lib/smithy-client/retry/client_rate_limiter.rb +142 -0
- data/lib/smithy-client/retry/quota.rb +58 -0
- data/lib/smithy-client/retry/standard.rb +52 -0
- data/lib/smithy-client/retry.rb +36 -0
- data/lib/smithy-client/rpc_v2_cbor/protocol.rb +38 -0
- data/lib/smithy-client/rpc_v2_cbor/request_builder.rb +76 -0
- data/lib/smithy-client/rpc_v2_cbor/response_parser.rb +86 -0
- data/lib/smithy-client/rpc_v2_cbor/response_stubber.rb +34 -0
- data/lib/smithy-client/service_error.rb +57 -0
- data/lib/smithy-client/signer.rb +16 -0
- data/lib/smithy-client/signers/anonymous.rb +13 -0
- data/lib/smithy-client/signers/http_api_key.rb +52 -0
- data/lib/smithy-client/signers/http_basic.rb +23 -0
- data/lib/smithy-client/signers/http_bearer.rb +19 -0
- data/lib/smithy-client/signers/http_digest.rb +21 -0
- data/lib/smithy-client/stubbing/data_applicator.rb +61 -0
- data/lib/smithy-client/stubbing/empty_stub.rb +69 -0
- data/lib/smithy-client/stubbing/endpoint_provider.rb +22 -0
- data/lib/smithy-client/stubbing/protocol.rb +29 -0
- data/lib/smithy-client/stubbing/stub_data.rb +25 -0
- data/lib/smithy-client/stubbing.rb +14 -0
- data/lib/smithy-client/stubs.rb +212 -0
- data/lib/smithy-client/util.rb +15 -0
- data/lib/smithy-client/waiters/poller.rb +93 -0
- data/lib/smithy-client/waiters/waiter.rb +113 -0
- data/lib/smithy-client.rb +66 -1
- metadata +163 -9
@@ -0,0 +1,213 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bigdecimal'
|
4
|
+
|
5
|
+
module Smithy
|
6
|
+
module Client
|
7
|
+
# @api private
|
8
|
+
class ParamValidator
|
9
|
+
include Smithy::Schema::Shapes
|
10
|
+
|
11
|
+
EXPECTED_GOT = 'expected %s to be %s, got class %s instead.'
|
12
|
+
|
13
|
+
def initialize(ref, validate_required: true)
|
14
|
+
@ref = ref
|
15
|
+
@validate_required = validate_required
|
16
|
+
end
|
17
|
+
|
18
|
+
# @param [Hash] params
|
19
|
+
# @param [String] context
|
20
|
+
# @return [void]
|
21
|
+
# @raise [ArgumentError] if the params are invalid
|
22
|
+
def validate!(params, context: 'params')
|
23
|
+
errors = []
|
24
|
+
structure(@ref, params, errors, context)
|
25
|
+
raise ArgumentError, error_messages(errors) unless errors.empty?
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
# rubocop:disable Metrics
|
31
|
+
def shape(ref, value, errors, context)
|
32
|
+
case ref.shape
|
33
|
+
when StructureShape then structure(ref, value, errors, context)
|
34
|
+
when ListShape then list(ref, value, errors, context)
|
35
|
+
when MapShape then map(ref, value, errors, context)
|
36
|
+
when DocumentShape then document(ref, value, errors, context)
|
37
|
+
when UnionShape then union(ref, value, errors, context)
|
38
|
+
when StringShape, EnumShape
|
39
|
+
errors << expected_got(context, 'a String', value) unless value.is_a?(String)
|
40
|
+
when IntegerShape, IntEnumShape
|
41
|
+
errors << expected_got(context, 'an Integer', value) unless value.is_a?(Integer)
|
42
|
+
when BigDecimalShape
|
43
|
+
errors << expected_got(context, 'a BigDecimal', value) unless value.is_a?(BigDecimal)
|
44
|
+
when FloatShape
|
45
|
+
errors << expected_got(context, 'a Float', value) unless value.is_a?(Float)
|
46
|
+
when TimestampShape
|
47
|
+
errors << expected_got(context, 'a Time object', value) unless value.is_a?(Time)
|
48
|
+
when BooleanShape
|
49
|
+
errors << expected_got(context, 'true or false', value) unless [true, false].include?(value)
|
50
|
+
when BlobShape
|
51
|
+
unless value.is_a?(String)
|
52
|
+
if streaming_input?(ref)
|
53
|
+
unless io_like?(value)
|
54
|
+
errors << expected_got(
|
55
|
+
context,
|
56
|
+
'a String or IO like object that supports read and rewind',
|
57
|
+
value
|
58
|
+
)
|
59
|
+
end
|
60
|
+
elsif !io_like?(value, require_size: true)
|
61
|
+
errors << expected_got(
|
62
|
+
context,
|
63
|
+
'a String or IO like object that supports read, rewind, and size',
|
64
|
+
value
|
65
|
+
)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
# rubocop:enable Metrics
|
71
|
+
|
72
|
+
def document(shape, value, errors, context)
|
73
|
+
document_types = [Hash, Array, Numeric, String, TrueClass, FalseClass, NilClass]
|
74
|
+
unless document_types.any? { |t| value.is_a?(t) }
|
75
|
+
errors << expected_got(context, "one of #{document_types.join(', ')}", value)
|
76
|
+
end
|
77
|
+
|
78
|
+
case value
|
79
|
+
when Hash
|
80
|
+
value.each do |k, v|
|
81
|
+
document(shape, v, errors, context + "[#{k}]")
|
82
|
+
end
|
83
|
+
when Array
|
84
|
+
value.each do |v|
|
85
|
+
document(shape, v, errors, context)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def list(ref, values, errors, context)
|
91
|
+
unless values.is_a?(Array)
|
92
|
+
errors << expected_got(context, 'an Array', values)
|
93
|
+
return
|
94
|
+
end
|
95
|
+
|
96
|
+
values.each.with_index do |value, index|
|
97
|
+
next unless value
|
98
|
+
|
99
|
+
shape(ref.shape.member, value, errors, context + "[#{index}]")
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def map(ref, values, errors, context)
|
104
|
+
unless values.is_a?(Hash)
|
105
|
+
errors << expected_got(context, 'a Hash', values)
|
106
|
+
return
|
107
|
+
end
|
108
|
+
|
109
|
+
values.each do |key, value|
|
110
|
+
shape(ref.shape.key, key, errors, "#{context} #{key.inspect} key")
|
111
|
+
next unless value
|
112
|
+
|
113
|
+
shape(ref.shape.value, value, errors, context + "[#{key.inspect}]")
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def member(ref, name, value, errors, context)
|
118
|
+
if ref.shape.member?(name)
|
119
|
+
member_ref = ref.shape.member(name)
|
120
|
+
shape(member_ref, value, errors, context + "[#{name.inspect}]")
|
121
|
+
else
|
122
|
+
errors << "unexpected value at #{context}[#{name.inspect}]"
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def structure(ref, values, errors, context)
|
127
|
+
return if ref.shape == Prelude::Unit
|
128
|
+
return unless valid_structure?(ref, values, errors, context)
|
129
|
+
|
130
|
+
validate_required_members(ref, values, errors, context) if @validate_required
|
131
|
+
values.each_pair do |name, value|
|
132
|
+
next if value.nil?
|
133
|
+
|
134
|
+
member(ref, name, value, errors, context)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def valid_structure?(ref, values, errors, context)
|
139
|
+
if !values.is_a?(Hash) && !values.is_a?(ref.shape.type)
|
140
|
+
errors << expected_got(context, 'a Hash', values)
|
141
|
+
return false
|
142
|
+
end
|
143
|
+
|
144
|
+
true
|
145
|
+
end
|
146
|
+
|
147
|
+
def union(ref, values, errors, context)
|
148
|
+
return unless valid_union?(ref, values, errors, context)
|
149
|
+
|
150
|
+
if values.is_a?(Schema::Union)
|
151
|
+
_name, member_ref = ref.shape.member_by_type(values.class)
|
152
|
+
shape(member_ref, values.value, errors, context)
|
153
|
+
else
|
154
|
+
values.each_pair do |name, value|
|
155
|
+
next if value.nil?
|
156
|
+
|
157
|
+
member(ref, name, value, errors, context)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def valid_union?(ref, values, errors, context)
|
163
|
+
return true if values.is_a?(ref.shape.type)
|
164
|
+
|
165
|
+
unless values.is_a?(Hash)
|
166
|
+
errors << expected_got(context, 'a Hash', values)
|
167
|
+
return false
|
168
|
+
end
|
169
|
+
return true if values.size <= 1
|
170
|
+
|
171
|
+
union_members = ref.shape.members.keys.join(', ')
|
172
|
+
error = "expected #{context} to be a Hash with one of #{union_members}, got #{values.size} keys instead."
|
173
|
+
errors << error
|
174
|
+
false
|
175
|
+
end
|
176
|
+
|
177
|
+
def validate_required_members(ref, values, errors, context)
|
178
|
+
ref.shape.members.each do |name, member_ref|
|
179
|
+
traits = member_ref.traits
|
180
|
+
next unless traits.key?('smithy.api#required') && !traits.key?('smithy.api#clientOptional')
|
181
|
+
|
182
|
+
if values[name].nil?
|
183
|
+
param = "#{context}[#{name.inspect}]"
|
184
|
+
errors << "missing required parameter #{param}"
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
def streaming_input?(ref)
|
190
|
+
ref.shape.traits.key?('smithy.api#streaming')
|
191
|
+
end
|
192
|
+
|
193
|
+
def io_like?(value, require_size: false)
|
194
|
+
value.respond_to?(:read) && value.respond_to?(:rewind) &&
|
195
|
+
(!require_size || value.respond_to?(:size))
|
196
|
+
end
|
197
|
+
|
198
|
+
def error_messages(errors)
|
199
|
+
if errors.size == 1
|
200
|
+
errors.first
|
201
|
+
else
|
202
|
+
prefix = "\n - "
|
203
|
+
"parameter validator found #{errors.size} errors:" +
|
204
|
+
prefix + errors.join(prefix)
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
def expected_got(context, expected, got)
|
209
|
+
format(EXPECTED_GOT, context, expected, got.class.name)
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
@@ -0,0 +1,144 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Smithy
|
4
|
+
module Client
|
5
|
+
# Base plugin that all plugins should inherit from.
|
6
|
+
#
|
7
|
+
# @example
|
8
|
+
# class MyPlugin < Plugin
|
9
|
+
# option :my_option, default: 'default'
|
10
|
+
#
|
11
|
+
# def add_handlers(handlers, _config)
|
12
|
+
# handlers.add(Handler)
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# class Handler < Client::Handler
|
16
|
+
# def call(context)
|
17
|
+
# ...
|
18
|
+
# end
|
19
|
+
# end
|
20
|
+
# end
|
21
|
+
class Plugin
|
22
|
+
extend HandlerBuilder
|
23
|
+
|
24
|
+
# @param [Configuration] config
|
25
|
+
# @return [void]
|
26
|
+
def add_options(config)
|
27
|
+
self.class.options.each do |option|
|
28
|
+
if option.default_block
|
29
|
+
config.add_option(option.name, &option.default_block)
|
30
|
+
else
|
31
|
+
config.add_option(option.name, option.default)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# @param [HandlerList] handlers
|
37
|
+
# @param [Struct] _config
|
38
|
+
# @return [void]
|
39
|
+
def add_handlers(handlers, _config)
|
40
|
+
handlers.copy_from(self.class.handlers)
|
41
|
+
end
|
42
|
+
|
43
|
+
# @param [Class<Base>] client_class
|
44
|
+
# @param [Hash] options
|
45
|
+
# @return [void]
|
46
|
+
def before_initialize(client_class, options)
|
47
|
+
self.class.before_initialize_hooks.each do |block|
|
48
|
+
block.call(client_class, options)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# @param [Base] client
|
53
|
+
# @return [void]
|
54
|
+
def after_initialize(client)
|
55
|
+
self.class.after_initialize_hooks.each do |block|
|
56
|
+
block.call(client)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
class << self
|
61
|
+
# (see PluginOption#initialize)
|
62
|
+
def option(name, options = {}, &block)
|
63
|
+
options[:default_block] = block if block_given?
|
64
|
+
self.options << PluginOption.new(name, options)
|
65
|
+
end
|
66
|
+
|
67
|
+
def before_initialize(&block)
|
68
|
+
before_initialize_hooks << block
|
69
|
+
end
|
70
|
+
|
71
|
+
def after_initialize(&block)
|
72
|
+
after_initialize_hooks << block
|
73
|
+
end
|
74
|
+
|
75
|
+
# @return [Array<PluginOption>]
|
76
|
+
def options
|
77
|
+
@options ||= []
|
78
|
+
end
|
79
|
+
|
80
|
+
# @return [HandlerList]
|
81
|
+
def handlers
|
82
|
+
@handlers ||= HandlerList.new
|
83
|
+
end
|
84
|
+
|
85
|
+
# @return [Array<Proc>]
|
86
|
+
def before_initialize_hooks
|
87
|
+
@before_initialize_hooks ||= []
|
88
|
+
end
|
89
|
+
|
90
|
+
# @return [Array<Proc>]
|
91
|
+
def after_initialize_hooks
|
92
|
+
@after_initialize_hooks ||= []
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# Holds the configuration for a plugin option.
|
97
|
+
class PluginOption
|
98
|
+
# @param [Symbol] name
|
99
|
+
# @param [Hash] options
|
100
|
+
# @option options [Object] :default
|
101
|
+
# @option options [Proc] :default_block Can also be set by passing a block.
|
102
|
+
# @option options [String] :doc_type
|
103
|
+
# @option options [String] :doc_default
|
104
|
+
# @option options [String] :docstring
|
105
|
+
# @option options [String] :rbs_type
|
106
|
+
def initialize(name, options = {})
|
107
|
+
@name = name
|
108
|
+
# prevent unstable object shapes by ensuring
|
109
|
+
# order and presence of instance variables
|
110
|
+
@default = nil
|
111
|
+
@default_block = nil
|
112
|
+
@doc_default = nil
|
113
|
+
@doc_type = nil
|
114
|
+
@docstring = nil
|
115
|
+
@rbs_type = nil
|
116
|
+
options.each_pair do |opt_name, opt_value|
|
117
|
+
send("#{opt_name}=", opt_value)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
# @return [Symbol]
|
122
|
+
attr_reader :name
|
123
|
+
|
124
|
+
# @return [Object, nil]
|
125
|
+
attr_accessor :default
|
126
|
+
|
127
|
+
# @return [Proc, nil]
|
128
|
+
attr_accessor :default_block
|
129
|
+
|
130
|
+
# @return [String, nil]
|
131
|
+
attr_accessor :doc_default
|
132
|
+
|
133
|
+
# @return [String, nil]
|
134
|
+
attr_accessor :doc_type
|
135
|
+
|
136
|
+
# @return [String, nil]
|
137
|
+
attr_accessor :docstring
|
138
|
+
|
139
|
+
# @return [String, nil]
|
140
|
+
attr_accessor :rbs_type
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Smithy
|
4
|
+
module Client
|
5
|
+
# A list of plugins.
|
6
|
+
class PluginList
|
7
|
+
include Enumerable
|
8
|
+
|
9
|
+
# Initializes the plugin list with the given plugins.
|
10
|
+
# @param [Array, Set, PluginList] plugins
|
11
|
+
def initialize(plugins = [])
|
12
|
+
@mutex = Mutex.new
|
13
|
+
@plugins = Set.new
|
14
|
+
if plugins.is_a?(PluginList)
|
15
|
+
plugins.send(:each_plugin) { |plugin| _add(plugin) }
|
16
|
+
else
|
17
|
+
plugins.each { |plugin| _add(plugin) }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# Enumerates the plugins.
|
22
|
+
# @return [Enumerator]
|
23
|
+
def each
|
24
|
+
each_plugin do |plugin_wrapper|
|
25
|
+
yield(plugin_wrapper.plugin)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# Adds and returns the `plugin`.
|
30
|
+
# @param [Plugin] plugin
|
31
|
+
# @return [void]
|
32
|
+
def add(plugin)
|
33
|
+
@mutex.synchronize do
|
34
|
+
_add(plugin)
|
35
|
+
end
|
36
|
+
nil
|
37
|
+
end
|
38
|
+
|
39
|
+
# Removes and returns the `plugin`.
|
40
|
+
# @param [Plugin] plugin
|
41
|
+
# @return [void]
|
42
|
+
def remove(plugin)
|
43
|
+
@mutex.synchronize do
|
44
|
+
@plugins.delete(PluginWrapper.new(plugin))
|
45
|
+
end
|
46
|
+
nil
|
47
|
+
end
|
48
|
+
|
49
|
+
# Replaces the existing list of plugins.
|
50
|
+
# @param [Array<Plugin>] plugins
|
51
|
+
# @return [void]
|
52
|
+
def set(plugins)
|
53
|
+
@mutex.synchronize do
|
54
|
+
@plugins.clear
|
55
|
+
plugins.each do |plugin|
|
56
|
+
_add(plugin)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
nil
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
# Not safe to call outside the mutex.
|
65
|
+
def _add(plugin)
|
66
|
+
@plugins << PluginWrapper.new(plugin)
|
67
|
+
end
|
68
|
+
|
69
|
+
# Yield each PluginDetail behind the mutex
|
70
|
+
def each_plugin(&block)
|
71
|
+
@mutex.synchronize do
|
72
|
+
@plugins.each(&block)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# A utility class that computes the canonical name for a plugin
|
77
|
+
# and defers requiring the plugin until the plugin class is
|
78
|
+
# required.
|
79
|
+
# @api private
|
80
|
+
class PluginWrapper
|
81
|
+
# @param [String, Symbol, Module, Class] plugin
|
82
|
+
def initialize(plugin)
|
83
|
+
case plugin
|
84
|
+
when Module
|
85
|
+
@canonical_name = plugin.name || plugin.object_id
|
86
|
+
@plugin = plugin
|
87
|
+
when Symbol, String
|
88
|
+
words = plugin.to_s.split('.')
|
89
|
+
@canonical_name = words.pop
|
90
|
+
@gem_name = words.empty? ? nil : words.join('.')
|
91
|
+
@plugin = nil
|
92
|
+
else
|
93
|
+
@canonical_name = plugin.object_id
|
94
|
+
@plugin = plugin
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# @return [String]
|
99
|
+
attr_reader :canonical_name
|
100
|
+
|
101
|
+
# @return [Class<Plugin>]
|
102
|
+
def plugin
|
103
|
+
@plugin ||= require_plugin
|
104
|
+
end
|
105
|
+
|
106
|
+
# Returns the given plugin if it is already a PluginWrapper.
|
107
|
+
def self.new(plugin)
|
108
|
+
if plugin.is_a?(self)
|
109
|
+
plugin
|
110
|
+
else
|
111
|
+
super
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# @return [Boolean]
|
116
|
+
# @api private
|
117
|
+
def eql?(other)
|
118
|
+
canonical_name == other.canonical_name
|
119
|
+
end
|
120
|
+
|
121
|
+
# @return [Integer]
|
122
|
+
# @api private
|
123
|
+
def hash
|
124
|
+
canonical_name.hash
|
125
|
+
end
|
126
|
+
|
127
|
+
private
|
128
|
+
|
129
|
+
# @return [Class<Plugin>]
|
130
|
+
def require_plugin
|
131
|
+
require(@gem_name) if @gem_name
|
132
|
+
plugin_class = Plugin
|
133
|
+
@canonical_name.split('::').each do |const_name|
|
134
|
+
plugin_class = plugin_class.const_get(const_name)
|
135
|
+
end
|
136
|
+
plugin_class
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../anonymous_provider'
|
4
|
+
require_relative '../identities/anonymous'
|
5
|
+
require_relative '../signers/anonymous'
|
6
|
+
require_relative '../auth_schemes/anonymous'
|
7
|
+
|
8
|
+
module Smithy
|
9
|
+
module Client
|
10
|
+
module Plugins
|
11
|
+
# @api private
|
12
|
+
class AnonymousAuth < Plugin
|
13
|
+
option(:anonymous_provider) do |_config|
|
14
|
+
AnonymousProvider.new
|
15
|
+
end
|
16
|
+
|
17
|
+
option(:anonymous_auth_scheme) do |_config|
|
18
|
+
Smithy::Client::AuthSchemes::Anonymous.new
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'openssl'
|
4
|
+
|
5
|
+
module Smithy
|
6
|
+
module Client
|
7
|
+
module Plugins
|
8
|
+
# @api private
|
9
|
+
class ChecksumRequired < Plugin
|
10
|
+
def add_handlers(handlers, _config)
|
11
|
+
# Ensure checksum is computed AFTER the request is built but BEFORE it is signed
|
12
|
+
handlers.add(Handler, priority: 15)
|
13
|
+
end
|
14
|
+
|
15
|
+
# @api private
|
16
|
+
class Handler < Client::Handler
|
17
|
+
CHUNK_SIZE = 1 * 1024 * 1024 # one MB
|
18
|
+
|
19
|
+
def call(context)
|
20
|
+
if checksum_required_operation?(context)
|
21
|
+
context.http_request.headers['Content-Md5'] ||= md5(context.http_request.body)
|
22
|
+
end
|
23
|
+
@handler.call(context)
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def checksum_required_operation?(context)
|
29
|
+
context.operation.traits.key?('smithy.api#httpChecksumRequired')
|
30
|
+
end
|
31
|
+
|
32
|
+
def md5(value)
|
33
|
+
md5 = OpenSSL::Digest.new('MD5')
|
34
|
+
update_in_chunks(md5, value)
|
35
|
+
md5.base64digest
|
36
|
+
end
|
37
|
+
|
38
|
+
def update_in_chunks(digest, io)
|
39
|
+
loop do
|
40
|
+
chunk = io.read(CHUNK_SIZE)
|
41
|
+
break unless chunk
|
42
|
+
|
43
|
+
digest.update(chunk)
|
44
|
+
end
|
45
|
+
io.rewind
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Smithy
|
4
|
+
module Client
|
5
|
+
module Plugins
|
6
|
+
# @api private
|
7
|
+
class ContentLength < Plugin
|
8
|
+
# @api private
|
9
|
+
class Handler < Client::Handler
|
10
|
+
# https://github.com/ruby/net-http/blob/master/lib/net/http/requests.rb
|
11
|
+
METHODS_WITH_BODY = Set.new(%w[POST PUT PATCH PROPFIND PROPPATCH MKCOL LOCK UNLOCK])
|
12
|
+
|
13
|
+
def call(context)
|
14
|
+
body = context.http_request.body
|
15
|
+
if body.respond_to?(:size) && METHODS_WITH_BODY.include?(context.http_request.http_method)
|
16
|
+
context.http_request.headers['Content-Length'] = body.size
|
17
|
+
end
|
18
|
+
@handler.call(context)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
handler(Handler, step: :sign, priority: 10)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Smithy
|
4
|
+
module Client
|
5
|
+
module Plugins
|
6
|
+
# @api private
|
7
|
+
class DefaultParams < Plugin
|
8
|
+
def add_handlers(handlers, _config)
|
9
|
+
handlers.add(Handler, step: :initialize)
|
10
|
+
end
|
11
|
+
|
12
|
+
# @api private
|
13
|
+
class Handler < Client::Handler
|
14
|
+
def call(context)
|
15
|
+
Client::DefaultParams.new(context.operation.input).apply(context.params)
|
16
|
+
@handler.call(context)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Smithy
|
4
|
+
module Client
|
5
|
+
module Plugins
|
6
|
+
# @api private
|
7
|
+
class HostPrefix < Plugin
|
8
|
+
option(
|
9
|
+
:disable_host_prefix_injection,
|
10
|
+
default: false,
|
11
|
+
doc_type: 'Boolean',
|
12
|
+
docstring: 'When `true`, the SDK will not prepend the modeled host prefix to the endpoint.'
|
13
|
+
) do |_config|
|
14
|
+
value = ENV['DISABLE_HOST_PREFIX_INJECTION'] || 'false'
|
15
|
+
Util.str_to_bool(value)
|
16
|
+
end
|
17
|
+
|
18
|
+
def after_initialize(client)
|
19
|
+
validate_disable_host_prefix_injection(client.config)
|
20
|
+
end
|
21
|
+
|
22
|
+
def validate_disable_host_prefix_injection(config)
|
23
|
+
return if [true, false].include?(config.disable_host_prefix_injection)
|
24
|
+
|
25
|
+
raise ArgumentError,
|
26
|
+
':disable_host_prefix_injection must be either `true` or `false`'
|
27
|
+
end
|
28
|
+
|
29
|
+
def add_handlers(handlers, config)
|
30
|
+
handlers.add(Handler, priority: 25) unless config.disable_host_prefix_injection
|
31
|
+
end
|
32
|
+
|
33
|
+
# @api private
|
34
|
+
class Handler < Smithy::Client::Handler
|
35
|
+
def call(context)
|
36
|
+
host_prefix = context.operation.traits.dig('smithy.api#endpoint', 'hostPrefix')
|
37
|
+
apply_host_prefix(context, host_prefix) if host_prefix
|
38
|
+
@handler.call(context)
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
# TODO: optimize this to collect all labels in one pass
|
44
|
+
def apply_host_prefix(context, host_prefix)
|
45
|
+
input = context.operation.input
|
46
|
+
prefix = host_prefix.gsub(/\{.+?}/) do |label|
|
47
|
+
label_value(input, label.delete('{}'), context.params)
|
48
|
+
end
|
49
|
+
context.http_request.endpoint.host = prefix + context.http_request.endpoint.host
|
50
|
+
end
|
51
|
+
|
52
|
+
def label_value(input, label, params)
|
53
|
+
name = nil
|
54
|
+
input.shape.members.each do |member_name, member_ref|
|
55
|
+
next unless member_ref.traits.key?('smithy.api#hostLabel')
|
56
|
+
next unless member_ref.member_name == label
|
57
|
+
|
58
|
+
name = member_name
|
59
|
+
end
|
60
|
+
raise ArgumentError, "#{label} is not a valid host label" if name.nil?
|
61
|
+
raise ArgumentError, "params[:#{name}] must not be nil or blank" if params[name].nil? || params[name].empty?
|
62
|
+
|
63
|
+
params[name]
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|