Skip to content

Improve testing of client features #290

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Apr 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 18 additions & 20 deletions gems/smithy-client/lib/smithy-client/plugins/protocol.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,25 @@ class Protocol < Plugin
docstring: 'The protocol to use for request serialization and response deserialization.'
)

def add_handlers(handlers, config)
return unless config.protocol
# @api private
class BuildHandler < Handler
def call(context)
context.config.protocol.build_request(context)
@handler.call(context)
end
end

# @api private
class ParseHandler < Handler
def call(context)
output = @handler.call(context)
output.error = context.config.protocol.parse_error(context) unless output.error
output.data = context.config.protocol.parse_data(context) unless output.error
output
end
end

def add_handlers(handlers, _config)
handlers.add(BuildHandler)
handlers.add(ParseHandler, step: :parse)
end
Expand Down Expand Up @@ -45,24 +61,6 @@ def resolve_default_protocol(client_class, options)
end
end
end

# @api private
class BuildHandler < Handler
def call(context)
context.config.protocol.build_request(context)
@handler.call(context)
end
end

# @api private
class ParseHandler < Handler
def call(context)
output = @handler.call(context)
output.error = context.config.protocol.parse_error(context) unless output.error
output.data = context.config.protocol.parse_data(context) unless output.error
output
end
end
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def event_stream?(shape)
end

def event_stream_shape?(shape)
shape.traits.include?('smithy.api#streaming') && shape.is_a?(Shapes::Union)
shape.traits.include?('smithy.api#streaming') && shape.is_a?(Schema::Shapes::UnionShape)
end

def build_url(context)
Expand Down
6 changes: 3 additions & 3 deletions gems/smithy-client/sig/smithy-client/auth_option.rbs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
module Smithy
module Client
class AuthOption
def initialize: (scheme_id: String, ?identity_properties: Hash[Symbol, untyped], ?signer_properties: Hash[Symbol, untyped]) -> void
def initialize: (scheme_id: String, ?identity_properties: Hash[String, untyped], ?signer_properties: Hash[String, untyped]) -> void
attr_reader scheme_id: String
attr_reader identity_properties: Hash[Symbol, untyped]
attr_reader signer_properties: Hash[Symbol, untyped]
attr_reader identity_properties: Hash[String, untyped]
attr_reader signer_properties: Hash[String, untyped]
end
end
end
4 changes: 2 additions & 2 deletions gems/smithy-client/spec/smithy-client/auth_option_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ module Smithy
module Client
describe AuthOption do
let(:scheme_id) { 'scheme_id' }
let(:identity_properties) { { identity: 'property' } }
let(:signer_properties) { { signer: 'property' } }
let(:identity_properties) { { 'identity' => 'property' } }
let(:signer_properties) { { 'signer' => 'property' } }

subject do
AuthOption.new(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ module Client
describe ParamConverter do
describe '#convert' do
it 'performs a deeply nested conversion of values' do
client_class = ClientHelper.sample_service
client_class = ClientHelper.sample_client
rules = client_class.const_get(:Schema).const_get(:SERVICE).operation(:operation).input
structure = client_class.const_get(:Types).const_get(:Structure)
union = client_class.const_get(:Types).const_get(:Union).const_get(:Structure)
Expand Down
12 changes: 6 additions & 6 deletions gems/smithy-client/spec/smithy-client/param_validator_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ module Smithy
module Client
describe ParamValidator do
let(:shapes) { SchemaHelper.sample_shapes }
let(:sample_service) { ClientHelper.sample_service(shapes: shapes) }
let(:service) { sample_service.const_get(:Schema).const_get(:SERVICE) }
let(:sample_client) { ClientHelper.sample_client(shapes: shapes) }
let(:service_shape) { sample_client.const_get(:Schema).const_get(:SERVICE) }

def validate(params, expected_errors = [])
schema = service.operation(:operation).input
schema = service_shape.operation(:operation).input
if expected_errors.empty?
ParamValidator.new(schema).validate!(params)
else
Expand Down Expand Up @@ -271,7 +271,7 @@ def match_errors(error, expected_errors)
end

it 'accepts a modeled type' do
structure = sample_service.const_get(:Types).const_get(:Structure).new({})
structure = sample_client.const_get(:Types).const_get(:Structure).new({})
validate({ structure: structure })
end
end
Expand Down Expand Up @@ -312,12 +312,12 @@ def match_errors(error, expected_errors)
end

it 'accepts a modeled type' do
union_structure = sample_service.const_get(:Types).const_get(:Union).const_get(:Structure)
union_structure = sample_client.const_get(:Types).const_get(:Union).const_get(:Structure)
validate({ union: union_structure.new({ string: 'string' }) })
end

it 'raises an error when given the wrong modeled type' do
union_structure = sample_service.const_get(:Types).const_get(:Union).const_get(:Structure)
union_structure = sample_client.const_get(:Types).const_get(:Union).const_get(:Structure)
validate({ union: union_structure.new({ structure: 'abc' }) },
'expected params[:union][:structure] to be a Hash, got class String instead.')
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,23 @@ module Smithy
module Client
module Plugins
describe HttpApiKeyAuth do
let(:sample_service) { ClientHelper.sample_service }
let(:shapes) { ClientHelper.sample_shapes }
let(:sample_client) { ClientHelper.sample_client(shapes: shapes) }

let(:client_class) do
client_class = sample_service.const_get(:Client)
client_class = sample_client.const_get(:Client)
client_class.clear_plugins
client_class.add_plugin(sample_service::Plugins::Endpoint)
client_class.add_plugin(sample_service::Plugins::Auth)
client_class.add_plugin(sample_client::Plugins::Auth)
client_class.add_plugin(sample_client::Plugins::Endpoint)
client_class.add_plugin(AnonymousAuth)
client_class.add_plugin(HttpApiKeyAuth)
client_class.add_plugin(Protocol)
client_class.add_plugin(SignRequests)
client_class.add_plugin(StubResponses)
client_class
end

let(:client) { client_class.new }
let(:client) { client_class.new(stub_responses: true) }

it 'adds an :http_api_key option to config' do
expect(client.config).to respond_to(:http_api_key)
Expand All @@ -32,20 +35,20 @@ module Plugins
end

it 'does not default a :http_api_key' do
client = client_class.new
expect(client.config.http_api_key).to be_nil
end

it 'does not default a :http_api_key_provider' do
client = client_class.new
expect(client.config.http_api_key_provider).to be_nil
end

it 'has a default :http_api_key when :stub_responses is true' do
client = client_class.new(stub_responses: true)
expect(client.config.http_api_key).to eq('stubbed-api-key')
end

it 'has a default :http_api_key_provider when :stub_responses is true' do
client = client_class.new(stub_responses: true)
provider = client.config.http_api_key_provider
expect(provider).to be_a(HttpApiKeyProvider)
expect(provider.identity({}).key).to eq('stubbed-api-key')
Expand All @@ -57,6 +60,35 @@ module Plugins
expect(provider).to be_a(HttpApiKeyProvider)
expect(provider.identity({}).key).to eq('api-key')
end

context 'signing' do
it 'signs in the header' do
shapes['smithy.ruby.tests#SampleClient']['traits']['smithy.api#httpApiKeyAuth'] = {
'name' => 'x-api-key', 'in' => 'header'
}

output = client.operation
expect(output.context.request.headers['x-api-key']).to eq('stubbed-api-key')
end

it 'signs in the header with a custom scheme' do
shapes['smithy.ruby.tests#SampleClient']['traits']['smithy.api#httpApiKeyAuth'] = {
'name' => 'x-api-key', 'in' => 'header', 'scheme' => 'ApiKey'
}

output = client.operation
expect(output.context.request.headers['x-api-key']).to eq('ApiKey stubbed-api-key')
end

it 'can sign on the query string' do
shapes['smithy.ruby.tests#SampleClient']['traits']['smithy.api#httpApiKeyAuth'] = {
'name' => 'x-api-key', 'in' => 'query'
}

output = client.operation
expect(output.context.request.endpoint.query).to include('x-api-key=stubbed-api-key')
end
end
end
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,23 @@ module Smithy
module Client
module Plugins
describe HttpBasicAuth do
let(:sample_service) { ClientHelper.sample_service }
let(:shapes) { ClientHelper.sample_shapes }
let(:sample_client) { ClientHelper.sample_client(shapes: shapes) }

let(:client_class) do
client_class = sample_service.const_get(:Client)
client_class = sample_client.const_get(:Client)
client_class.clear_plugins
client_class.add_plugin(sample_service::Plugins::Endpoint)
client_class.add_plugin(sample_service::Plugins::Auth)
client_class.add_plugin(sample_client::Plugins::Auth)
client_class.add_plugin(sample_client::Plugins::Endpoint)
client_class.add_plugin(AnonymousAuth)
client_class.add_plugin(HttpBasicAuth)
client_class.add_plugin(Protocol)
client_class.add_plugin(SignRequests)
client_class.add_plugin(StubResponses)
client_class
end

let(:client) { client_class.new }
let(:client) { client_class.new(stub_responses: true) }

it 'adds an :http_login_username option to config' do
expect(client.config).to respond_to(:http_login_username)
Expand All @@ -36,22 +39,22 @@ module Plugins
end

it 'does not default an :http_login_username or :http_login_password' do
client = client_class.new
expect(client.config.http_login_username).to be_nil
expect(client.config.http_login_password).to be_nil
end

it 'does not default a :http_login_provider' do
client = client_class.new
expect(client.config.http_login_provider).to be_nil
end

it 'has a default :http_login_username and :http_login_password when :stub_responses is true' do
client = client_class.new(stub_responses: true)
expect(client.config.http_login_username).to eq('stubbed-username')
expect(client.config.http_login_password).to eq('stubbed-password')
end

it 'has a default :http_login_provider when :stub_responses is true' do
client = client_class.new(stub_responses: true)
provider = client.config.http_login_provider
expect(provider).to be_a(HttpLoginProvider)
identity = provider.identity({})
Expand All @@ -77,6 +80,17 @@ module Plugins
provider = client.config.http_login_provider
expect(provider).to be_nil
end

context 'signing' do
it 'signs in the header' do
shapes['smithy.ruby.tests#SampleClient']['traits']['smithy.api#httpBasicAuth'] = {}

output = client.operation
identity_string = "#{client.config.http_login_username}:#{client.config.http_login_password}"
expect(output.context.request.headers['Authorization'])
.to eq("Basic #{Base64.strict_encode64(identity_string)}")
end
end
end
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,23 @@ module Smithy
module Client
module Plugins
describe HttpBearerAuth do
let(:sample_service) { ClientHelper.sample_service }
let(:shapes) { ClientHelper.sample_shapes }
let(:sample_client) { ClientHelper.sample_client(shapes: shapes) }

let(:client_class) do
client_class = sample_service.const_get(:Client)
client_class = sample_client.const_get(:Client)
client_class.clear_plugins
client_class.add_plugin(sample_service::Plugins::Endpoint)
client_class.add_plugin(sample_service::Plugins::Auth)
client_class.add_plugin(sample_client::Plugins::Auth)
client_class.add_plugin(sample_client::Plugins::Endpoint)
client_class.add_plugin(AnonymousAuth)
client_class.add_plugin(HttpBearerAuth)
client_class.add_plugin(Protocol)
client_class.add_plugin(SignRequests)
client_class.add_plugin(StubResponses)
client_class
end

let(:client) { client_class.new }
let(:client) { client_class.new(stub_responses: true) }

it 'adds an :http_bearer_token option to config' do
expect(client.config).to respond_to(:http_bearer_token)
Expand All @@ -32,20 +35,20 @@ module Plugins
end

it 'does not default a :http_bearer_token' do
client = client_class.new
expect(client.config.http_bearer_token).to be_nil
end

it 'does not default a :http_bearer_provider' do
client = client_class.new
expect(client.config.http_bearer_provider).to be_nil
end

it 'has a default :http_bearer_token when :stub_responses is true' do
client = client_class.new(stub_responses: true)
expect(client.config.http_bearer_token).to eq('stubbed-bearer-token')
end

it 'has a default :http_bearer_provider when :stub_responses is true' do
client = client_class.new(stub_responses: true)
provider = client.config.http_bearer_provider
expect(provider).to be_a(HttpBearerProvider)
expect(provider.identity({}).token).to eq('stubbed-bearer-token')
Expand All @@ -57,6 +60,16 @@ module Plugins
expect(provider).to be_a(HttpBearerProvider)
expect(provider.identity({}).token).to eq('bearer')
end

context 'signing' do
it 'signs in the header' do
shapes['smithy.ruby.tests#SampleClient']['traits']['smithy.api#httpBearerAuth'] = {}

output = client.operation
expect(output.context.request.headers['Authorization'])
.to eq("Bearer #{client.config.http_bearer_token}")
end
end
end
end
end
Expand Down
Loading
Loading