From 1c1e520d459daa300d5907d5a6a185c55ef53099 Mon Sep 17 00:00:00 2001 From: Alex Woods Date: Tue, 30 Jul 2024 08:32:51 -0700 Subject: [PATCH] Test protocol (#213) --- codegen/projections/white_label/.yardopts | 2 +- .../white_label/lib/white_label/builders.rb | 227 +++++++++++++++ .../white_label/lib/white_label/parsers.rb | 170 ++++++++++++ .../white_label/lib/white_label/stubs.rb | 146 +++++++++- .../white_label/spec/endpoints_spec.rb | 2 +- ...tion.java => TestProtocolIntegration.java} | 8 +- ...erator.java => TestProtocolGenerator.java} | 9 +- .../protocol/generators/BuilderGenerator.java | 248 +++++++++++++++-- .../protocol/generators/ErrorsGenerator.java | 2 +- .../protocol/generators/ParserGenerator.java | 260 +++++++++++++++++- .../protocol/generators/StubsGenerator.java | 257 ++++++++++++++++- .../EndpointTestServiceTransformer.java | 4 +- ...amazon.smithy.ruby.codegen.RubyIntegration | 2 +- .../integration-specs/endpoints_spec.rb | 2 +- .../model/component-test/main.smithy | 6 +- ...e-protocol.smithy => test-protocol.smithy} | 2 +- .../generators/BuilderGeneratorBase.java | 1 - ...tocolTrait.java => TestProtocolTrait.java} | 12 +- ...re.amazon.smithy.model.traits.TraitService | 2 +- 19 files changed, 1280 insertions(+), 82 deletions(-) rename codegen/smithy-ruby-codegen-test-utils/src/main/java/software/amazon/smithy/ruby/codegen/integrations/{FakeProtocolIntegration.java => TestProtocolIntegration.java} (79%) rename codegen/smithy-ruby-codegen-test-utils/src/main/java/software/amazon/smithy/ruby/codegen/protocol/{FakeProtocolGenerator.java => TestProtocolGenerator.java} (88%) rename codegen/smithy-ruby-codegen-test/model/{fake-protocol.smithy => test-protocol.smithy} (83%) rename codegen/smithy-ruby-codegen/src/main/java/software/amazon/smithy/ruby/codegen/traits/{FakeProtocolTrait.java => TestProtocolTrait.java} (76%) diff --git a/codegen/projections/white_label/.yardopts b/codegen/projections/white_label/.yardopts index 0e503773d..aa3e6134b 100644 --- a/codegen/projections/white_label/.yardopts +++ b/codegen/projections/white_label/.yardopts @@ -1,2 +1,2 @@ ---title "FakeProtocol Test Service" +--title "TestProtocol Test Service" --hide-api private diff --git a/codegen/projections/white_label/lib/white_label/builders.rb b/codegen/projections/white_label/lib/white_label/builders.rb index a5346ee54..e2f70d2f3 100644 --- a/codegen/projections/white_label/lib/white_label/builders.rb +++ b/codegen/projections/white_label/lib/white_label/builders.rb @@ -7,118 +7,297 @@ # # WARNING ABOUT GENERATED CODE +require 'stringio' + module WhiteLabel # @api private module Builders class CustomAuth def self.build(http_req, input:) + http_req.http_method = 'POST' + http_req.append_path('/') + http_req.headers['Content-Type'] = 'application/json' + http_req.headers['X-Rpc-Target'] = 'WhiteLabel.CustomAuth' + data = {} + http_req.body = ::StringIO.new(Hearth::JSON.dump(data)) end end class DataplaneEndpoint def self.build(http_req, input:) + http_req.http_method = 'POST' + http_req.append_path('/') + http_req.headers['Content-Type'] = 'application/json' + http_req.headers['X-Rpc-Target'] = 'WhiteLabel.DataplaneEndpoint' + data = {} + http_req.body = ::StringIO.new(Hearth::JSON.dump(data)) end end class Defaults + def self.build(input) + data = {} + data['String'] = input.string unless input.string.nil? + data['struct'] = Struct.build(input.struct) unless input.struct.nil? + data['unRequiredNumber'] = input.un_required_number unless input.un_required_number.nil? + data['unRequiredBool'] = input.un_required_bool unless input.un_required_bool.nil? + data['Number'] = input.number unless input.number.nil? + data['Bool'] = input.bool unless input.bool.nil? + data['hello'] = input.hello unless input.hello.nil? + data['simpleEnum'] = input.simple_enum unless input.simple_enum.nil? + data['valuedEnum'] = input.valued_enum unless input.valued_enum.nil? + data['IntEnum'] = input.int_enum unless input.int_enum.nil? + data['nullDocument'] = input.null_document unless input.null_document.nil? + data['stringDocument'] = input.string_document unless input.string_document.nil? + data['booleanDocument'] = input.boolean_document unless input.boolean_document.nil? + data['numbersDocument'] = input.numbers_document unless input.numbers_document.nil? + data['listDocument'] = input.list_document unless input.list_document.nil? + data['mapDocument'] = input.map_document unless input.map_document.nil? + data['ListOfStrings'] = ListOfStrings.build(input.list_of_strings) unless input.list_of_strings.nil? + data['MapOfStrings'] = MapOfStrings.build(input.map_of_strings) unless input.map_of_strings.nil? + data['Iso8601Timestamp'] = Hearth::TimeHelper.to_date_time(input.iso8601_timestamp) unless input.iso8601_timestamp.nil? + data['EpochTimestamp'] = Hearth::TimeHelper.to_epoch_seconds(input.epoch_timestamp).to_i unless input.epoch_timestamp.nil? + data + end end class DefaultsTest def self.build(http_req, input:) + http_req.http_method = 'POST' + http_req.append_path('/') + http_req.headers['Content-Type'] = 'application/json' + http_req.headers['X-Rpc-Target'] = 'WhiteLabel.DefaultsTest' + data = {} + data['defaults'] = Defaults.build(input.defaults) unless input.defaults.nil? + http_req.body = ::StringIO.new(Hearth::JSON.dump(data)) end end class EndpointOperation def self.build(http_req, input:) + http_req.http_method = 'POST' + http_req.append_path('/') + http_req.headers['Content-Type'] = 'application/json' + http_req.headers['X-Rpc-Target'] = 'WhiteLabel.EndpointOperation' + data = {} + http_req.body = ::StringIO.new(Hearth::JSON.dump(data)) end end class EndpointWithHostLabelOperation def self.build(http_req, input:) + http_req.http_method = 'POST' + http_req.append_path('/') + http_req.headers['Content-Type'] = 'application/json' + http_req.headers['X-Rpc-Target'] = 'WhiteLabel.EndpointWithHostLabelOperation' + data = {} + data['labelMember'] = input.label_member unless input.label_member.nil? + http_req.body = ::StringIO.new(Hearth::JSON.dump(data)) end end class HttpApiKeyAuth def self.build(http_req, input:) + http_req.http_method = 'POST' + http_req.append_path('/') + http_req.headers['Content-Type'] = 'application/json' + http_req.headers['X-Rpc-Target'] = 'WhiteLabel.HttpApiKeyAuth' + data = {} + http_req.body = ::StringIO.new(Hearth::JSON.dump(data)) end end class HttpBasicAuth def self.build(http_req, input:) + http_req.http_method = 'POST' + http_req.append_path('/') + http_req.headers['Content-Type'] = 'application/json' + http_req.headers['X-Rpc-Target'] = 'WhiteLabel.HttpBasicAuth' + data = {} + http_req.body = ::StringIO.new(Hearth::JSON.dump(data)) end end class HttpBearerAuth def self.build(http_req, input:) + http_req.http_method = 'POST' + http_req.append_path('/') + http_req.headers['Content-Type'] = 'application/json' + http_req.headers['X-Rpc-Target'] = 'WhiteLabel.HttpBearerAuth' + data = {} + http_req.body = ::StringIO.new(Hearth::JSON.dump(data)) end end class HttpDigestAuth def self.build(http_req, input:) + http_req.http_method = 'POST' + http_req.append_path('/') + http_req.headers['Content-Type'] = 'application/json' + http_req.headers['X-Rpc-Target'] = 'WhiteLabel.HttpDigestAuth' + data = {} + http_req.body = ::StringIO.new(Hearth::JSON.dump(data)) end end class KitchenSink def self.build(http_req, input:) + http_req.http_method = 'POST' + http_req.append_path('/') + http_req.headers['Content-Type'] = 'application/json' + http_req.headers['X-Rpc-Target'] = 'WhiteLabel.KitchenSink' + data = {} + data['String'] = input.string unless input.string.nil? + data['SimpleEnum'] = input.simple_enum unless input.simple_enum.nil? + data['ValuedEnum'] = input.valued_enum unless input.valued_enum.nil? + data['Struct'] = Struct.build(input.struct) unless input.struct.nil? + data['Document'] = input.document unless input.document.nil? + data['ListOfStrings'] = ListOfStrings.build(input.list_of_strings) unless input.list_of_strings.nil? + data['ListOfStructs'] = ListOfStructs.build(input.list_of_structs) unless input.list_of_structs.nil? + data['MapOfStrings'] = MapOfStrings.build(input.map_of_strings) unless input.map_of_strings.nil? + data['MapOfStructs'] = MapOfStructs.build(input.map_of_structs) unless input.map_of_structs.nil? + data['Union'] = Union.build(input.union) unless input.union.nil? + http_req.body = ::StringIO.new(Hearth::JSON.dump(data)) end end class ListOfStrings + def self.build(input) + data = [] + input.each do |element| + data << element unless element.nil? + end + data + end end class ListOfStructs + def self.build(input) + data = [] + input.each do |element| + data << Struct.build(element) unless element.nil? + end + data + end end class MapOfStrings + def self.build(input) + data = {} + input.each do |key, value| + data[key] = value unless value.nil? + end + data + end end class MapOfStructs + def self.build(input) + data = {} + input.each do |key, value| + data[key] = Struct.build(value) unless value.nil? + end + data + end end class MixinTest def self.build(http_req, input:) + http_req.http_method = 'POST' + http_req.append_path('/') + http_req.headers['Content-Type'] = 'application/json' + http_req.headers['X-Rpc-Target'] = 'WhiteLabel.MixinTest' + data = {} + data['userId'] = input.user_id unless input.user_id.nil? + http_req.body = ::StringIO.new(Hearth::JSON.dump(data)) end end class NoAuth def self.build(http_req, input:) + http_req.http_method = 'POST' + http_req.append_path('/') + http_req.headers['Content-Type'] = 'application/json' + http_req.headers['X-Rpc-Target'] = 'WhiteLabel.NoAuth' + data = {} + http_req.body = ::StringIO.new(Hearth::JSON.dump(data)) end end class OptionalAuth def self.build(http_req, input:) + http_req.http_method = 'POST' + http_req.append_path('/') + http_req.headers['Content-Type'] = 'application/json' + http_req.headers['X-Rpc-Target'] = 'WhiteLabel.OptionalAuth' + data = {} + http_req.body = ::StringIO.new(Hearth::JSON.dump(data)) end end class OrderedAuth def self.build(http_req, input:) + http_req.http_method = 'POST' + http_req.append_path('/') + http_req.headers['Content-Type'] = 'application/json' + http_req.headers['X-Rpc-Target'] = 'WhiteLabel.OrderedAuth' + data = {} + http_req.body = ::StringIO.new(Hearth::JSON.dump(data)) end end class PaginatorsTest def self.build(http_req, input:) + http_req.http_method = 'POST' + http_req.append_path('/') + http_req.headers['Content-Type'] = 'application/json' + http_req.headers['X-Rpc-Target'] = 'WhiteLabel.PaginatorsTest' + data = {} + data['nextToken'] = input.next_token unless input.next_token.nil? + http_req.body = ::StringIO.new(Hearth::JSON.dump(data)) end end class PaginatorsTestWithItems def self.build(http_req, input:) + http_req.http_method = 'POST' + http_req.append_path('/') + http_req.headers['Content-Type'] = 'application/json' + http_req.headers['X-Rpc-Target'] = 'WhiteLabel.PaginatorsTestWithItems' + data = {} + data['nextToken'] = input.next_token unless input.next_token.nil? + http_req.body = ::StringIO.new(Hearth::JSON.dump(data)) end end class RelativeMiddleware def self.build(http_req, input:) + http_req.http_method = 'POST' + http_req.append_path('/') + http_req.headers['Content-Type'] = 'application/json' + http_req.headers['X-Rpc-Target'] = 'WhiteLabel.RelativeMiddleware' + data = {} + http_req.body = ::StringIO.new(Hearth::JSON.dump(data)) end end class RequestCompression def self.build(http_req, input:) + http_req.http_method = 'POST' + http_req.append_path('/') + http_req.headers['Content-Type'] = 'application/json' + http_req.headers['X-Rpc-Target'] = 'WhiteLabel.RequestCompression' http_req.body = StringIO.new(input.body || '') end end class RequestCompressionStreaming def self.build(http_req, input:) + http_req.http_method = 'POST' + http_req.append_path('/') + http_req.headers['Content-Type'] = 'application/json' + http_req.headers['X-Rpc-Target'] = 'WhiteLabel.RequestCompressionStreaming' http_req.body = input.body http_req.headers['Transfer-Encoding'] = 'chunked' http_req.headers['Content-Type'] = 'application/octet-stream' @@ -127,11 +306,22 @@ def self.build(http_req, input:) class ResourceEndpoint def self.build(http_req, input:) + http_req.http_method = 'POST' + http_req.append_path('/') + http_req.headers['Content-Type'] = 'application/json' + http_req.headers['X-Rpc-Target'] = 'WhiteLabel.ResourceEndpoint' + data = {} + data['resourceUrl'] = input.resource_url unless input.resource_url.nil? + http_req.body = ::StringIO.new(Hearth::JSON.dump(data)) end end class Streaming def self.build(http_req, input:) + http_req.http_method = 'POST' + http_req.append_path('/') + http_req.headers['Content-Type'] = 'application/json' + http_req.headers['X-Rpc-Target'] = 'WhiteLabel.Streaming' http_req.body = input.stream http_req.headers['Transfer-Encoding'] = 'chunked' http_req.headers['Content-Type'] = 'application/octet-stream' @@ -140,24 +330,61 @@ def self.build(http_req, input:) class StreamingWithLength def self.build(http_req, input:) + http_req.http_method = 'POST' + http_req.append_path('/') + http_req.headers['Content-Type'] = 'application/json' + http_req.headers['X-Rpc-Target'] = 'WhiteLabel.StreamingWithLength' http_req.body = input.stream http_req.headers['Content-Type'] = 'application/octet-stream' end end class Struct + def self.build(input) + data = {} + data['value'] = input.value unless input.value.nil? + data + end end class Union + def self.build(input) + data = {} + case input + when Types::Union::String + data['String'] = input + when Types::Union::Struct + data['Struct'] = (Struct.build(input) unless input.nil?) + else + raise ArgumentError, + "Expected input to be one of the subclasses of Types::Union" + end + + data + end end class WaitersTest def self.build(http_req, input:) + http_req.http_method = 'POST' + http_req.append_path('/') + http_req.headers['Content-Type'] = 'application/json' + http_req.headers['X-Rpc-Target'] = 'WhiteLabel.WaitersTest' + data = {} + data['Status'] = input.status unless input.status.nil? + http_req.body = ::StringIO.new(Hearth::JSON.dump(data)) end end class Operation____PaginatorsTestWithBadNames def self.build(http_req, input:) + http_req.http_method = 'POST' + http_req.append_path('/') + http_req.headers['Content-Type'] = 'application/json' + http_req.headers['X-Rpc-Target'] = 'WhiteLabel.__PaginatorsTestWithBadNames' + data = {} + data['__nextToken'] = input.member___next_token unless input.member___next_token.nil? + http_req.body = ::StringIO.new(Hearth::JSON.dump(data)) end end end diff --git a/codegen/projections/white_label/lib/white_label/parsers.rb b/codegen/projections/white_label/lib/white_label/parsers.rb index 5efb7874d..5b5aea699 100644 --- a/codegen/projections/white_label/lib/white_label/parsers.rb +++ b/codegen/projections/white_label/lib/white_label/parsers.rb @@ -15,6 +15,10 @@ module Parsers class ClientError def self.parse(http_resp) data = Types::ClientError.new + body = http_resp.body.read + return data if body.empty? + map = Hearth::JSON.parse(body) + data.message = map['Message'] data end end @@ -22,6 +26,9 @@ def self.parse(http_resp) class CustomAuth def self.parse(http_resp) data = Types::CustomAuthOutput.new + body = http_resp.body.read + return data if body.empty? + map = Hearth::JSON.parse(body) data end end @@ -29,6 +36,9 @@ def self.parse(http_resp) class DataplaneEndpoint def self.parse(http_resp) data = Types::DataplaneEndpointOutput.new + body = http_resp.body.read + return data if body.empty? + map = Hearth::JSON.parse(body) data end end @@ -36,6 +46,29 @@ def self.parse(http_resp) class DefaultsTest def self.parse(http_resp) data = Types::DefaultsTestOutput.new + body = http_resp.body.read + return data if body.empty? + map = Hearth::JSON.parse(body) + data.string = map['String'] + data.struct = (Struct.parse(map['struct']) unless map['struct'].nil?) + data.un_required_number = map['unRequiredNumber'] + data.un_required_bool = map['unRequiredBool'] + data.number = map['Number'] + data.bool = map['Bool'] + data.hello = map['hello'] + data.simple_enum = map['simpleEnum'] + data.valued_enum = map['valuedEnum'] + data.int_enum = map['IntEnum'] + data.null_document = map['nullDocument'] + data.string_document = map['stringDocument'] + data.boolean_document = map['booleanDocument'] + data.numbers_document = map['numbersDocument'] + data.list_document = map['listDocument'] + data.map_document = map['mapDocument'] + data.list_of_strings = (ListOfStrings.parse(map['ListOfStrings']) unless map['ListOfStrings'].nil?) + data.map_of_strings = (MapOfStrings.parse(map['MapOfStrings']) unless map['MapOfStrings'].nil?) + data.iso8601_timestamp = Time.parse(map['Iso8601Timestamp']) if map['Iso8601Timestamp'] + data.epoch_timestamp = Time.at(map['EpochTimestamp'].to_i) if map['EpochTimestamp'] data end end @@ -43,6 +76,9 @@ def self.parse(http_resp) class EndpointOperation def self.parse(http_resp) data = Types::EndpointOperationOutput.new + body = http_resp.body.read + return data if body.empty? + map = Hearth::JSON.parse(body) data end end @@ -50,6 +86,9 @@ def self.parse(http_resp) class EndpointWithHostLabelOperation def self.parse(http_resp) data = Types::EndpointWithHostLabelOperationOutput.new + body = http_resp.body.read + return data if body.empty? + map = Hearth::JSON.parse(body) data end end @@ -57,6 +96,9 @@ def self.parse(http_resp) class HttpApiKeyAuth def self.parse(http_resp) data = Types::HttpApiKeyAuthOutput.new + body = http_resp.body.read + return data if body.empty? + map = Hearth::JSON.parse(body) data end end @@ -64,6 +106,9 @@ def self.parse(http_resp) class HttpBasicAuth def self.parse(http_resp) data = Types::HttpBasicAuthOutput.new + body = http_resp.body.read + return data if body.empty? + map = Hearth::JSON.parse(body) data end end @@ -71,6 +116,9 @@ def self.parse(http_resp) class HttpBearerAuth def self.parse(http_resp) data = Types::HttpBearerAuthOutput.new + body = http_resp.body.read + return data if body.empty? + map = Hearth::JSON.parse(body) data end end @@ -78,35 +126,85 @@ def self.parse(http_resp) class HttpDigestAuth def self.parse(http_resp) data = Types::HttpDigestAuthOutput.new + body = http_resp.body.read + return data if body.empty? + map = Hearth::JSON.parse(body) data end end class Items + def self.parse(list) + list.map do |value| + value unless value.nil? + end + end end class KitchenSink def self.parse(http_resp) data = Types::KitchenSinkOutput.new + body = http_resp.body.read + return data if body.empty? + map = Hearth::JSON.parse(body) + data.string = map['String'] + data.simple_enum = map['SimpleEnum'] + data.valued_enum = map['ValuedEnum'] + data.struct = (Struct.parse(map['Struct']) unless map['Struct'].nil?) + data.document = map['Document'] + data.list_of_strings = (ListOfStrings.parse(map['ListOfStrings']) unless map['ListOfStrings'].nil?) + data.list_of_structs = (ListOfStructs.parse(map['ListOfStructs']) unless map['ListOfStructs'].nil?) + data.map_of_strings = (MapOfStrings.parse(map['MapOfStrings']) unless map['MapOfStrings'].nil?) + data.map_of_structs = (MapOfStructs.parse(map['MapOfStructs']) unless map['MapOfStructs'].nil?) + data.union = (Union.parse(map['Union']) unless map['Union'].nil?) data end end class ListOfStrings + def self.parse(list) + list.map do |value| + value unless value.nil? + end + end end class ListOfStructs + def self.parse(list) + list.map do |value| + Struct.parse(value) unless value.nil? + end + end end class MapOfStrings + def self.parse(map) + data = {} + map.map do |key, value| + data[key] = value unless value.nil? + end + data + end end class MapOfStructs + def self.parse(map) + data = {} + map.map do |key, value| + data[key] = Struct.parse(value) unless value.nil? + end + data + end end class MixinTest def self.parse(http_resp) data = Types::MixinTestOutput.new + body = http_resp.body.read + return data if body.empty? + map = Hearth::JSON.parse(body) + data.username = map['username'] + data.user_id = map['userId'] data end end @@ -114,6 +212,9 @@ def self.parse(http_resp) class NoAuth def self.parse(http_resp) data = Types::NoAuthOutput.new + body = http_resp.body.read + return data if body.empty? + map = Hearth::JSON.parse(body) data end end @@ -121,6 +222,9 @@ def self.parse(http_resp) class OptionalAuth def self.parse(http_resp) data = Types::OptionalAuthOutput.new + body = http_resp.body.read + return data if body.empty? + map = Hearth::JSON.parse(body) data end end @@ -128,6 +232,9 @@ def self.parse(http_resp) class OrderedAuth def self.parse(http_resp) data = Types::OrderedAuthOutput.new + body = http_resp.body.read + return data if body.empty? + map = Hearth::JSON.parse(body) data end end @@ -135,6 +242,11 @@ def self.parse(http_resp) class PaginatorsTest def self.parse(http_resp) data = Types::PaginatorsTestOperationOutput.new + body = http_resp.body.read + return data if body.empty? + map = Hearth::JSON.parse(body) + data.next_token = map['nextToken'] + data.items = (Items.parse(map['items']) unless map['items'].nil?) data end end @@ -142,6 +254,11 @@ def self.parse(http_resp) class PaginatorsTestWithItems def self.parse(http_resp) data = Types::PaginatorsTestWithItemsOutput.new + body = http_resp.body.read + return data if body.empty? + map = Hearth::JSON.parse(body) + data.next_token = map['nextToken'] + data.items = (Items.parse(map['items']) unless map['items'].nil?) data end end @@ -149,6 +266,9 @@ def self.parse(http_resp) class RelativeMiddleware def self.parse(http_resp) data = Types::RelativeMiddlewareOutput.new + body = http_resp.body.read + return data if body.empty? + map = Hearth::JSON.parse(body) data end end @@ -156,6 +276,9 @@ def self.parse(http_resp) class RequestCompression def self.parse(http_resp) data = Types::RequestCompressionOutput.new + body = http_resp.body.read + return data if body.empty? + map = Hearth::JSON.parse(body) data end end @@ -163,6 +286,9 @@ def self.parse(http_resp) class RequestCompressionStreaming def self.parse(http_resp) data = Types::RequestCompressionStreamingOutput.new + body = http_resp.body.read + return data if body.empty? + map = Hearth::JSON.parse(body) data end end @@ -170,17 +296,28 @@ def self.parse(http_resp) class ResourceEndpoint def self.parse(http_resp) data = Types::ResourceEndpointOutput.new + body = http_resp.body.read + return data if body.empty? + map = Hearth::JSON.parse(body) data end end class ResultWrapper + def self.parse(map) + data = Types::ResultWrapper.new + data.member___123next_token = map['__123nextToken'] + return data + end end # Error Parser for ServerError class ServerError def self.parse(http_resp) data = Types::ServerError.new + body = http_resp.body.read + return data if body.empty? + map = Hearth::JSON.parse(body) data end end @@ -196,19 +333,47 @@ def self.parse(http_resp) class StreamingWithLength def self.parse(http_resp) data = Types::StreamingWithLengthOutput.new + body = http_resp.body.read + return data if body.empty? + map = Hearth::JSON.parse(body) data end end class Struct + def self.parse(map) + data = Types::Struct.new + data.value = map['value'] + return data + end end class Union + def self.parse(map) + return nil if map.nil? + + map.delete('__type') + key, value = map.compact.flatten + case key + when 'String' + value = value + Types::Union::String.new(value) if value + when 'Struct' + value = (Struct.parse(value) unless value.nil?) + Types::Union::Struct.new(value) if value + else + Types::Union::Unknown.new(name: key, value: value) + end + end end class WaitersTest def self.parse(http_resp) data = Types::WaitersTestOutput.new + body = http_resp.body.read + return data if body.empty? + map = Hearth::JSON.parse(body) + data.status = map['Status'] data end end @@ -216,6 +381,11 @@ def self.parse(http_resp) class Operation____PaginatorsTestWithBadNames def self.parse(http_resp) data = Types::Struct____PaginatorsTestWithBadNamesOutput.new + body = http_resp.body.read + return data if body.empty? + map = Hearth::JSON.parse(body) + data.member___wrapper = (ResultWrapper.parse(map['__wrapper']) unless map['__wrapper'].nil?) + data.member___items = (Items.parse(map['__items']) unless map['__items'].nil?) data end end diff --git a/codegen/projections/white_label/lib/white_label/stubs.rb b/codegen/projections/white_label/lib/white_label/stubs.rb index 6f1aac623..278232a54 100644 --- a/codegen/projections/white_label/lib/white_label/stubs.rb +++ b/codegen/projections/white_label/lib/white_label/stubs.rb @@ -7,6 +7,8 @@ # # WARNING ABOUT GENERATED CODE +require 'stringio' + module WhiteLabel # @api private module Stubs @@ -26,6 +28,13 @@ def self.default(visited = []) } end + def self.stub(http_resp, stub:) + http_resp.status = 400 + data = {} + data['__type'] = 'smithy.ruby.tests#ClientError' + data['Message'] = stub.message unless stub.message.nil? + http_resp.body = ::StringIO.new(Hearth::JSON.dump(data)) + end end class CustomAuth @@ -44,6 +53,7 @@ def self.default(visited = []) def self.stub(http_resp, stub:) data = {} + http_resp.body = ::StringIO.new(Hearth::JSON.dump(data)) http_resp.status = 200 end end @@ -64,6 +74,7 @@ def self.default(visited = []) def self.stub(http_resp, stub:) data = {} + http_resp.body = ::StringIO.new(Hearth::JSON.dump(data)) http_resp.status = 200 end end @@ -104,6 +115,27 @@ def self.default(visited = []) def self.stub(http_resp, stub:) data = {} + data['String'] = stub.string unless stub.string.nil? + data['struct'] = Struct.stub(stub.struct) unless stub.struct.nil? + data['unRequiredNumber'] = stub.un_required_number unless stub.un_required_number.nil? + data['unRequiredBool'] = stub.un_required_bool unless stub.un_required_bool.nil? + data['Number'] = stub.number unless stub.number.nil? + data['Bool'] = stub.bool unless stub.bool.nil? + data['hello'] = stub.hello unless stub.hello.nil? + data['simpleEnum'] = stub.simple_enum unless stub.simple_enum.nil? + data['valuedEnum'] = stub.valued_enum unless stub.valued_enum.nil? + data['IntEnum'] = stub.int_enum unless stub.int_enum.nil? + data['nullDocument'] = stub.null_document unless stub.null_document.nil? + data['stringDocument'] = stub.string_document unless stub.string_document.nil? + data['booleanDocument'] = stub.boolean_document unless stub.boolean_document.nil? + data['numbersDocument'] = stub.numbers_document unless stub.numbers_document.nil? + data['listDocument'] = stub.list_document unless stub.list_document.nil? + data['mapDocument'] = stub.map_document unless stub.map_document.nil? + data['ListOfStrings'] = ListOfStrings.stub(stub.list_of_strings) unless stub.list_of_strings.nil? + data['MapOfStrings'] = MapOfStrings.stub(stub.map_of_strings) unless stub.map_of_strings.nil? + data['Iso8601Timestamp'] = Hearth::TimeHelper.to_date_time(stub.iso8601_timestamp) unless stub.iso8601_timestamp.nil? + data['EpochTimestamp'] = Hearth::TimeHelper.to_epoch_seconds(stub.epoch_timestamp).to_i unless stub.epoch_timestamp.nil? + http_resp.body = ::StringIO.new(Hearth::JSON.dump(data)) http_resp.status = 200 end end @@ -136,6 +168,7 @@ def self.default(visited = []) def self.stub(http_resp, stub:) data = {} + http_resp.body = ::StringIO.new(Hearth::JSON.dump(data)) http_resp.status = 200 end end @@ -156,6 +189,7 @@ def self.default(visited = []) def self.stub(http_resp, stub:) data = {} + http_resp.body = ::StringIO.new(Hearth::JSON.dump(data)) http_resp.status = 200 end end @@ -176,6 +210,7 @@ def self.default(visited = []) def self.stub(http_resp, stub:) data = {} + http_resp.body = ::StringIO.new(Hearth::JSON.dump(data)) http_resp.status = 200 end end @@ -196,6 +231,7 @@ def self.default(visited = []) def self.stub(http_resp, stub:) data = {} + http_resp.body = ::StringIO.new(Hearth::JSON.dump(data)) http_resp.status = 200 end end @@ -216,6 +252,7 @@ def self.default(visited = []) def self.stub(http_resp, stub:) data = {} + http_resp.body = ::StringIO.new(Hearth::JSON.dump(data)) http_resp.status = 200 end end @@ -236,6 +273,7 @@ def self.default(visited = []) def self.stub(http_resp, stub:) data = {} + http_resp.body = ::StringIO.new(Hearth::JSON.dump(data)) http_resp.status = 200 end end @@ -249,6 +287,14 @@ def self.default(visited = []) ] end + def self.stub(stub) + stub ||= [] + data = [] + stub.each do |element| + data << element unless element.nil? + end + data + end end class KitchenSink @@ -277,6 +323,17 @@ def self.default(visited = []) def self.stub(http_resp, stub:) data = {} + data['String'] = stub.string unless stub.string.nil? + data['SimpleEnum'] = stub.simple_enum unless stub.simple_enum.nil? + data['ValuedEnum'] = stub.valued_enum unless stub.valued_enum.nil? + data['Struct'] = Struct.stub(stub.struct) unless stub.struct.nil? + data['Document'] = stub.document unless stub.document.nil? + data['ListOfStrings'] = ListOfStrings.stub(stub.list_of_strings) unless stub.list_of_strings.nil? + data['ListOfStructs'] = ListOfStructs.stub(stub.list_of_structs) unless stub.list_of_structs.nil? + data['MapOfStrings'] = MapOfStrings.stub(stub.map_of_strings) unless stub.map_of_strings.nil? + data['MapOfStructs'] = MapOfStructs.stub(stub.map_of_structs) unless stub.map_of_structs.nil? + data['Union'] = Union.stub(stub.union) unless stub.union.nil? + http_resp.body = ::StringIO.new(Hearth::JSON.dump(data)) http_resp.status = 200 end end @@ -290,6 +347,14 @@ def self.default(visited = []) ] end + def self.stub(stub) + stub ||= [] + data = [] + stub.each do |element| + data << element unless element.nil? + end + data + end end class ListOfStructs @@ -301,6 +366,14 @@ def self.default(visited = []) ] end + def self.stub(stub) + stub ||= [] + data = [] + stub.each do |element| + data << Struct.stub(element) unless element.nil? + end + data + end end class MapOfStrings @@ -312,6 +385,14 @@ def self.default(visited = []) } end + def self.stub(stub) + stub ||= {} + data = {} + stub.each do |key, value| + data[key] = value unless value.nil? + end + data + end end class MapOfStructs @@ -323,6 +404,14 @@ def self.default(visited = []) } end + def self.stub(stub) + stub ||= {} + data = {} + stub.each do |key, value| + data[key] = Struct.stub(value) unless value.nil? + end + data + end end class MixinTest @@ -343,6 +432,9 @@ def self.default(visited = []) def self.stub(http_resp, stub:) data = {} + data['username'] = stub.username unless stub.username.nil? + data['userId'] = stub.user_id unless stub.user_id.nil? + http_resp.body = ::StringIO.new(Hearth::JSON.dump(data)) http_resp.status = 200 end end @@ -363,6 +455,7 @@ def self.default(visited = []) def self.stub(http_resp, stub:) data = {} + http_resp.body = ::StringIO.new(Hearth::JSON.dump(data)) http_resp.status = 200 end end @@ -383,6 +476,7 @@ def self.default(visited = []) def self.stub(http_resp, stub:) data = {} + http_resp.body = ::StringIO.new(Hearth::JSON.dump(data)) http_resp.status = 200 end end @@ -403,6 +497,7 @@ def self.default(visited = []) def self.stub(http_resp, stub:) data = {} + http_resp.body = ::StringIO.new(Hearth::JSON.dump(data)) http_resp.status = 200 end end @@ -425,6 +520,9 @@ def self.default(visited = []) def self.stub(http_resp, stub:) data = {} + data['nextToken'] = stub.next_token unless stub.next_token.nil? + data['items'] = Items.stub(stub.items) unless stub.items.nil? + http_resp.body = ::StringIO.new(Hearth::JSON.dump(data)) http_resp.status = 200 end end @@ -447,6 +545,9 @@ def self.default(visited = []) def self.stub(http_resp, stub:) data = {} + data['nextToken'] = stub.next_token unless stub.next_token.nil? + data['items'] = Items.stub(stub.items) unless stub.items.nil? + http_resp.body = ::StringIO.new(Hearth::JSON.dump(data)) http_resp.status = 200 end end @@ -467,6 +568,7 @@ def self.default(visited = []) def self.stub(http_resp, stub:) data = {} + http_resp.body = ::StringIO.new(Hearth::JSON.dump(data)) http_resp.status = 200 end end @@ -487,6 +589,7 @@ def self.default(visited = []) def self.stub(http_resp, stub:) data = {} + http_resp.body = ::StringIO.new(Hearth::JSON.dump(data)) http_resp.status = 200 end end @@ -507,6 +610,7 @@ def self.default(visited = []) def self.stub(http_resp, stub:) data = {} + http_resp.body = ::StringIO.new(Hearth::JSON.dump(data)) http_resp.status = 200 end end @@ -527,6 +631,7 @@ def self.default(visited = []) def self.stub(http_resp, stub:) data = {} + http_resp.body = ::StringIO.new(Hearth::JSON.dump(data)) http_resp.status = 200 end end @@ -540,6 +645,12 @@ def self.default(visited = []) } end + def self.stub(stub) + stub ||= Types::ResultWrapper.new + data = {} + data['__123nextToken'] = stub.member___123next_token unless stub.member___123next_token.nil? + data + end end class ServerError @@ -556,6 +667,12 @@ def self.default(visited = []) } end + def self.stub(http_resp, stub:) + http_resp.status = 500 + data = {} + data['__type'] = 'smithy.ruby.tests#ServerError' + http_resp.body = ::StringIO.new(Hearth::JSON.dump(data)) + end end class Streaming @@ -574,9 +691,8 @@ def self.default(visited = []) end def self.stub(http_resp, stub:) - data = {} - http_resp.status = 200 IO.copy_stream(stub.stream, http_resp.body) + http_resp.status = 200 end end @@ -596,6 +712,7 @@ def self.default(visited = []) def self.stub(http_resp, stub:) data = {} + http_resp.body = ::StringIO.new(Hearth::JSON.dump(data)) http_resp.status = 200 end end @@ -609,6 +726,12 @@ def self.default(visited = []) } end + def self.stub(stub) + stub ||= Types::Struct.new + data = {} + data['value'] = stub.value unless stub.value.nil? + data + end end class Union @@ -620,6 +743,20 @@ def self.default(visited = []) } end + def self.stub(stub) + data = {} + case stub + when Types::Union::String + data['String'] = stub.__getobj__ + when Types::Union::Struct + data['Struct'] = (Struct.stub(stub.__getobj__) unless stub.__getobj__.nil?) + else + raise ArgumentError, + "Expected input to be one of the subclasses of Types::Union" + end + + data + end end class WaitersTest @@ -639,6 +776,8 @@ def self.default(visited = []) def self.stub(http_resp, stub:) data = {} + data['Status'] = stub.status unless stub.status.nil? + http_resp.body = ::StringIO.new(Hearth::JSON.dump(data)) http_resp.status = 200 end end @@ -661,6 +800,9 @@ def self.default(visited = []) def self.stub(http_resp, stub:) data = {} + data['__wrapper'] = ResultWrapper.stub(stub.member___wrapper) unless stub.member___wrapper.nil? + data['__items'] = Items.stub(stub.member___items) unless stub.member___items.nil? + http_resp.body = ::StringIO.new(Hearth::JSON.dump(data)) http_resp.status = 200 end end diff --git a/codegen/projections/white_label/spec/endpoints_spec.rb b/codegen/projections/white_label/spec/endpoints_spec.rb index e969ff3e2..efa6936cd 100644 --- a/codegen/projections/white_label/spec/endpoints_spec.rb +++ b/codegen/projections/white_label/spec/endpoints_spec.rb @@ -46,7 +46,7 @@ module WhiteLabel it 'prepends the label to the host' do proc = proc do |context| expect(context.request.uri.to_s) - .to eq("https://foo.#{label}.data.whitelabel.com") + .to eq("https://foo.#{label}.data.whitelabel.com/") end interceptor = Hearth::Interceptor.new(read_before_transmit: proc) client.endpoint_with_host_label_operation( diff --git a/codegen/smithy-ruby-codegen-test-utils/src/main/java/software/amazon/smithy/ruby/codegen/integrations/FakeProtocolIntegration.java b/codegen/smithy-ruby-codegen-test-utils/src/main/java/software/amazon/smithy/ruby/codegen/integrations/TestProtocolIntegration.java similarity index 79% rename from codegen/smithy-ruby-codegen-test-utils/src/main/java/software/amazon/smithy/ruby/codegen/integrations/FakeProtocolIntegration.java rename to codegen/smithy-ruby-codegen-test-utils/src/main/java/software/amazon/smithy/ruby/codegen/integrations/TestProtocolIntegration.java index 309f9d399..dfd651904 100644 --- a/codegen/smithy-ruby-codegen-test-utils/src/main/java/software/amazon/smithy/ruby/codegen/integrations/FakeProtocolIntegration.java +++ b/codegen/smithy-ruby-codegen-test-utils/src/main/java/software/amazon/smithy/ruby/codegen/integrations/TestProtocolIntegration.java @@ -19,17 +19,17 @@ import java.util.List; import software.amazon.smithy.ruby.codegen.ProtocolGenerator; import software.amazon.smithy.ruby.codegen.RubyIntegration; -import software.amazon.smithy.ruby.codegen.protocol.FakeProtocolGenerator; +import software.amazon.smithy.ruby.codegen.protocol.TestProtocolGenerator; import software.amazon.smithy.utils.SmithyInternalApi; /** - * Provide support for whitelabel testing (implements fakeProtocol). + * Provide support for whitelabel testing (implements testProtocol). */ @SmithyInternalApi -public class FakeProtocolIntegration implements RubyIntegration { +public class TestProtocolIntegration implements RubyIntegration { @Override public List getProtocolGenerators() { - return Arrays.asList(new FakeProtocolGenerator()); + return Arrays.asList(new TestProtocolGenerator()); } } diff --git a/codegen/smithy-ruby-codegen-test-utils/src/main/java/software/amazon/smithy/ruby/codegen/protocol/FakeProtocolGenerator.java b/codegen/smithy-ruby-codegen-test-utils/src/main/java/software/amazon/smithy/ruby/codegen/protocol/TestProtocolGenerator.java similarity index 88% rename from codegen/smithy-ruby-codegen-test-utils/src/main/java/software/amazon/smithy/ruby/codegen/protocol/FakeProtocolGenerator.java rename to codegen/smithy-ruby-codegen-test-utils/src/main/java/software/amazon/smithy/ruby/codegen/protocol/TestProtocolGenerator.java index f2f358dc3..5948d809b 100644 --- a/codegen/smithy-ruby-codegen-test-utils/src/main/java/software/amazon/smithy/ruby/codegen/protocol/FakeProtocolGenerator.java +++ b/codegen/smithy-ruby-codegen-test-utils/src/main/java/software/amazon/smithy/ruby/codegen/protocol/TestProtocolGenerator.java @@ -24,18 +24,19 @@ import software.amazon.smithy.ruby.codegen.protocol.generators.ErrorsGenerator; import software.amazon.smithy.ruby.codegen.protocol.generators.ParserGenerator; import software.amazon.smithy.ruby.codegen.protocol.generators.StubsGenerator; -import software.amazon.smithy.ruby.codegen.traits.FakeProtocolTrait; +import software.amazon.smithy.ruby.codegen.traits.TestProtocolTrait; import software.amazon.smithy.utils.SmithyInternalApi; /** - * Protocol Implementation for fakeProtocol - used by the whitelabel codegen-test. + * Protocol Implementation for testProtocol - used by the whitelabel codegen-test. + * TestProtocol implements a jsonRPC like protocol and is intended only for testing. */ @SmithyInternalApi -public class FakeProtocolGenerator implements ProtocolGenerator { +public class TestProtocolGenerator implements ProtocolGenerator { @Override public ShapeId getProtocol() { - return FakeProtocolTrait.ID; + return TestProtocolTrait.ID; } @Override diff --git a/codegen/smithy-ruby-codegen-test-utils/src/main/java/software/amazon/smithy/ruby/codegen/protocol/generators/BuilderGenerator.java b/codegen/smithy-ruby-codegen-test-utils/src/main/java/software/amazon/smithy/ruby/codegen/protocol/generators/BuilderGenerator.java index 77dc1e30e..b43e1324f 100644 --- a/codegen/smithy-ruby-codegen-test-utils/src/main/java/software/amazon/smithy/ruby/codegen/protocol/generators/BuilderGenerator.java +++ b/codegen/smithy-ruby-codegen-test-utils/src/main/java/software/amazon/smithy/ruby/codegen/protocol/generators/BuilderGenerator.java @@ -16,68 +16,268 @@ package software.amazon.smithy.ruby.codegen.protocol.generators; import java.util.Optional; +import java.util.stream.Stream; +import software.amazon.smithy.model.shapes.BlobShape; +import software.amazon.smithy.model.shapes.DoubleShape; +import software.amazon.smithy.model.shapes.FloatShape; import software.amazon.smithy.model.shapes.ListShape; import software.amazon.smithy.model.shapes.MapShape; import software.amazon.smithy.model.shapes.MemberShape; import software.amazon.smithy.model.shapes.OperationShape; import software.amazon.smithy.model.shapes.Shape; +import software.amazon.smithy.model.shapes.ShapeVisitor; import software.amazon.smithy.model.shapes.StructureShape; +import software.amazon.smithy.model.shapes.TimestampShape; import software.amazon.smithy.model.shapes.UnionShape; +import software.amazon.smithy.model.traits.EventHeaderTrait; import software.amazon.smithy.model.traits.HttpPayloadTrait; +import software.amazon.smithy.model.traits.SparseTrait; +import software.amazon.smithy.model.traits.StreamingTrait; +import software.amazon.smithy.model.traits.TimestampFormatTrait; import software.amazon.smithy.ruby.codegen.GenerationContext; +import software.amazon.smithy.ruby.codegen.Hearth; +import software.amazon.smithy.ruby.codegen.RubyImportContainer; import software.amazon.smithy.ruby.codegen.generators.BuilderGeneratorBase; +import software.amazon.smithy.ruby.codegen.traits.NoSerializeTrait; import software.amazon.smithy.ruby.codegen.util.Streaming; +import software.amazon.smithy.ruby.codegen.util.TimestampFormat; /** - * FakeProtocol builder generator. + * TestProtocol builder generator. */ + public class BuilderGenerator extends BuilderGeneratorBase { - /** - * @param context generation context. - */ + public BuilderGenerator(GenerationContext context) { super(context); } + private void renderMemberBuilders(Shape s) { + renderMemberBuilders(s, "input"); + } + private void renderMemberBuilders(Shape s, String input) { + //remove members marked NoSerialize + Stream serializeMembers = s.members().stream() + .filter(NoSerializeTrait.excludeNoSerializeMembers()) + .filter((m) -> !StreamingTrait.isEventStream(model, m) && !m.hasTrait(EventHeaderTrait.class)); + + serializeMembers.forEach((member) -> { + Shape target = model.expectShape(member.getTarget()); + String dataName = "'" + member.getMemberName() + "'"; + String dataSetter = "data[" + dataName + "] = "; + String inputGetter = input + "." + symbolProvider.toMemberName(member); + target.accept(new MemberSerializer(member, dataSetter, inputGetter, true)); + }); + } + @Override protected void renderOperationBuildMethod(OperationShape operation, Shape inputShape) { - writer.openBlock("def self.build(http_req, input:)"); - - // checks for Payload member - Optional httpPayloadMember = inputShape.members() - .stream() - .filter((m) -> m.hasTrait(HttpPayloadTrait.class)) - .findFirst(); - if (httpPayloadMember.isPresent()) { - if (Streaming.isStreaming(model, inputShape)) { - renderStreamingBodyBuilder(inputShape); - } else { - // only works for String/Blob/Number data types - writer.write("http_req.body = StringIO.new(input.$L || '')", - symbolProvider.toMemberName(httpPayloadMember.get())); - } - } - writer.closeBlock("end"); - } + String target = context.service().getId().getName() + "." + operation.getId().getName(); + writer + .openBlock("def self.build(http_req, input:)") + .write("http_req.http_method = 'POST'") + .write("http_req.append_path('/')") + .write("http_req.headers['Content-Type'] = 'application/json'") + .write("http_req.headers['X-Rpc-Target'] = '$L'", target) + .call(() -> { + // checks for Payload member + Optional httpPayloadMember = inputShape.members() + .stream() + .filter((m) -> m.hasTrait(HttpPayloadTrait.class)) + .findFirst(); + if (httpPayloadMember.isPresent()) { + if (Streaming.isStreaming(model, inputShape)) { + renderStreamingBodyBuilder(inputShape); + } else { + // only works for String/Blob/Number data types + writer.write("http_req.body = StringIO.new(input.$L || '')", + symbolProvider.toMemberName(httpPayloadMember.get())); + } + } else { + writer.write("data = {}"); + renderMemberBuilders(inputShape); + writer.write("http_req.body = $T.new($T.dump(data))", + RubyImportContainer.STRING_IO, Hearth.JSON); + } + }) + .closeBlock("end"); + } @Override protected void renderStructureBuildMethod(StructureShape shape) { - + writer + .openBlock("def self.build(input)") + .write("data = {}") + .call(() -> renderMemberBuilders(shape)) + .write("data") + .closeBlock("end"); } @Override protected void renderListBuildMethod(ListShape shape) { - + writer + .openBlock("def self.build(input)") + .write("data = []") + .openBlock("input.each do |element|") + .call(() -> { + Shape memberTarget = model.expectShape(shape.getMember().getTarget()); + memberTarget.accept(new MemberSerializer(shape.getMember(), "data << ", "element", + !shape.hasTrait(SparseTrait.class))); + }) + .closeBlock("end") + .write("data") + .closeBlock("end"); } @Override protected void renderUnionBuildMethod(UnionShape shape) { + writer + .openBlock("def self.build(input)") + .write("data = {}") + .write("case input"); + + shape.members().forEach((member) -> { + writer + .write("when $T", context.symbolProvider().toSymbol(member)) + .indent(); + renderUnionMemberBuilder(shape, member); + writer.dedent(); + }); + writer.openBlock("else") + .write("raise ArgumentError,\n\"Expected input to be one of the subclasses of $T\"", + context.symbolProvider().toSymbol(shape)) + .closeBlock("end") + .write("") + .write("data") + .closeBlock("end"); + } + private void renderUnionMemberBuilder(UnionShape shape, MemberShape member) { + Shape target = model.expectShape(member.getTarget()); + String dataSetter = "data['" + member.getMemberName() + "'] = "; + if (target.isUnionShape()) { + writer.write("input = input.__getobj__"); // need to avoid infinite recursion + } + target.accept(new MemberSerializer(member, dataSetter, "input", false)); } @Override protected void renderMapBuildMethod(MapShape shape) { + writer + .openBlock("def self.build(input)") + .write("data = {}") + .openBlock("input.each do |key, value|") + .call(() -> { + Shape valueTarget = model.expectShape(shape.getValue().getTarget()); + valueTarget.accept(new MemberSerializer(shape.getValue(), "data[key] = ", "value", + !shape.hasTrait(SparseTrait.class))); + }) + .closeBlock("end") + .write("data") + .closeBlock("end"); + } + + private class MemberSerializer extends ShapeVisitor.Default { + + private final String inputGetter; + private final String dataSetter; + private final MemberShape memberShape; + private final boolean checkRequired; + + MemberSerializer(MemberShape memberShape, + String dataSetter, String inputGetter, boolean checkRequired) { + this.inputGetter = inputGetter; + this.dataSetter = dataSetter; + this.memberShape = memberShape; + this.checkRequired = checkRequired; + } + + private String checkRequired() { + if (this.checkRequired) { + return " unless " + inputGetter + ".nil?"; + } else { + return ""; + } + } + + @Override + protected Void getDefault(Shape shape) { + writer.write("$L$L$L", dataSetter, inputGetter, checkRequired()); + return null; + } + + private void rubyFloat() { + writer.write("$1L$4T.serialize($2L)$3L", + dataSetter, inputGetter, checkRequired(), Hearth.NUMBER_HELPER); + } + + @Override + public Void doubleShape(DoubleShape shape) { + rubyFloat(); + return null; + } + + @Override + public Void floatShape(FloatShape shape) { + rubyFloat(); + return null; + } + + @Override + public Void blobShape(BlobShape shape) { + writer.write("$L$T::strict_encode64($L).strip$L", + dataSetter, RubyImportContainer.BASE64, inputGetter, checkRequired()); + return null; + } + + @Override + public Void timestampShape(TimestampShape shape) { + writer.write("$L$L$L", + dataSetter, + TimestampFormat.serializeTimestamp( + shape, memberShape, inputGetter, TimestampFormatTrait.Format.EPOCH_SECONDS, false), + checkRequired()); + return null; + } + + /** + * For complex shapes, simply delegate to their builder. + */ + private void defaultComplexSerializer(Shape shape) { + if (checkRequired) { + writer.write("$1L$2L.build($3L) unless $3L.nil?", + dataSetter, symbolProvider.toSymbol(shape).getName(), + inputGetter); + } else { + writer.write("$1L($2L.build($3L) unless $3L.nil?)", + dataSetter, symbolProvider.toSymbol(shape).getName(), + inputGetter); + } + } + + @Override + public Void listShape(ListShape shape) { + defaultComplexSerializer(shape); + return null; + } + + @Override + public Void mapShape(MapShape shape) { + defaultComplexSerializer(shape); + return null; + } + @Override + public Void structureShape(StructureShape shape) { + defaultComplexSerializer(shape); + return null; + } + + @Override + public Void unionShape(UnionShape shape) { + defaultComplexSerializer(shape); + return null; + } } } diff --git a/codegen/smithy-ruby-codegen-test-utils/src/main/java/software/amazon/smithy/ruby/codegen/protocol/generators/ErrorsGenerator.java b/codegen/smithy-ruby-codegen-test-utils/src/main/java/software/amazon/smithy/ruby/codegen/protocol/generators/ErrorsGenerator.java index da804937e..826cfc891 100644 --- a/codegen/smithy-ruby-codegen-test-utils/src/main/java/software/amazon/smithy/ruby/codegen/protocol/generators/ErrorsGenerator.java +++ b/codegen/smithy-ruby-codegen-test-utils/src/main/java/software/amazon/smithy/ruby/codegen/protocol/generators/ErrorsGenerator.java @@ -19,7 +19,7 @@ import software.amazon.smithy.ruby.codegen.generators.ErrorsGeneratorBase; /** - * FakeProtocol ErrorGenerator. + * TestProtocol ErrorGenerator. */ public class ErrorsGenerator extends ErrorsGeneratorBase { diff --git a/codegen/smithy-ruby-codegen-test-utils/src/main/java/software/amazon/smithy/ruby/codegen/protocol/generators/ParserGenerator.java b/codegen/smithy-ruby-codegen-test-utils/src/main/java/software/amazon/smithy/ruby/codegen/protocol/generators/ParserGenerator.java index a47279230..949ec219d 100644 --- a/codegen/smithy-ruby-codegen-test-utils/src/main/java/software/amazon/smithy/ruby/codegen/protocol/generators/ParserGenerator.java +++ b/codegen/smithy-ruby-codegen-test-utils/src/main/java/software/amazon/smithy/ruby/codegen/protocol/generators/ParserGenerator.java @@ -15,52 +15,284 @@ package software.amazon.smithy.ruby.codegen.protocol.generators; +import java.util.Optional; +import java.util.stream.Stream; +import software.amazon.smithy.model.shapes.BlobShape; +import software.amazon.smithy.model.shapes.DoubleShape; +import software.amazon.smithy.model.shapes.FloatShape; import software.amazon.smithy.model.shapes.ListShape; import software.amazon.smithy.model.shapes.MapShape; import software.amazon.smithy.model.shapes.MemberShape; +import software.amazon.smithy.model.shapes.OperationShape; import software.amazon.smithy.model.shapes.Shape; +import software.amazon.smithy.model.shapes.ShapeVisitor; import software.amazon.smithy.model.shapes.StructureShape; +import software.amazon.smithy.model.shapes.TimestampShape; import software.amazon.smithy.model.shapes.UnionShape; -import software.amazon.smithy.model.traits.StreamingTrait; +import software.amazon.smithy.model.traits.HttpPayloadTrait; +import software.amazon.smithy.model.traits.JsonNameTrait; +import software.amazon.smithy.model.traits.SparseTrait; +import software.amazon.smithy.model.traits.TimestampFormatTrait; import software.amazon.smithy.ruby.codegen.GenerationContext; -import software.amazon.smithy.ruby.codegen.generators.RestParserGeneratorBase; +import software.amazon.smithy.ruby.codegen.Hearth; +import software.amazon.smithy.ruby.codegen.RubyImportContainer; +import software.amazon.smithy.ruby.codegen.generators.ParserGeneratorBase; +import software.amazon.smithy.ruby.codegen.traits.NoSerializeTrait; +import software.amazon.smithy.ruby.codegen.util.Streaming; +import software.amazon.smithy.ruby.codegen.util.TimestampFormat; -public class ParserGenerator extends RestParserGeneratorBase { +public class ParserGenerator extends ParserGeneratorBase { public ParserGenerator(GenerationContext context) { super(context); } - @Override - protected void renderPayloadBodyParser(Shape outputShape, MemberShape payloadMember, Shape target) { - if (target.hasTrait(StreamingTrait.class)) { - renderStreamingBodyParser(outputShape); - } - } + private void renderMemberParsers(Shape s) { + //remove members w/ http traits or marked NoSerialize + Stream serializeMembers = s.members().stream() + .filter(NoSerializeTrait.excludeNoSerializeMembers()); - @Override - protected void renderBodyParser(Shape outputShape) { + serializeMembers.forEach((member) -> { + Shape target = model.expectShape(member.getTarget()); + String dataName = symbolProvider.toMemberName(member); + String dataSetter = "data." + dataName + " = "; + String jsonName = member.getMemberName(); + String valueGetter = "map['" + jsonName + "']"; + target.accept(new MemberDeserializer(member, dataSetter, valueGetter, false)); + }); } @Override protected void renderUnionParseMethod(UnionShape s) { + writer + .openBlock("def self.parse(map)") + .write("return nil if map.nil?\n") + .write("map.delete('__type')") + .write("key, value = map.compact.flatten") + .write("case key") + .call(() -> { + s.members().forEach((member) -> { + writer + .write("when '$L'", unionMemberDataName(s, member)) + .indent() + .call(() -> { + renderUnionMemberParser(s, member); + }) + .write("$T.new(value) if value", context.symbolProvider().toSymbol(member)) + .dedent(); + }); + }) + .openBlock("else") + .write("$T::Unknown.new(name: key, value: value)", context.symbolProvider().toSymbol(s)) + .closeBlock("end") // end of case + .closeBlock("end"); + } + + private String unionMemberDataName(UnionShape s, MemberShape member) { + String dataName = member.getMemberName(); + String jsonName = dataName; + if (member.hasTrait(JsonNameTrait.class)) { + jsonName = member.getTrait(JsonNameTrait.class).get().getValue(); + } + return jsonName; + } + private void renderUnionMemberParser(UnionShape s, MemberShape member) { + Shape target = model.expectShape(member.getTarget()); + target.accept(new MemberDeserializer(member, "value = ", + "value", false)); } @Override protected void renderMapParseMethod(MapShape s) { - + writer + .openBlock("def self.parse(map)") + .write("data = {}") + .openBlock("map.map do |key, value|") + .call(() -> { + Shape valueTarget = model.expectShape(s.getValue().getTarget()); + valueTarget + .accept(new MemberDeserializer(s.getValue(), + "data[key] = ", "value", + !s.hasTrait(SparseTrait.class))); + }) + .closeBlock("end") + .write("data") + .closeBlock("end"); } @Override protected void renderListParseMethod(ListShape s) { - + writer + .openBlock("def self.parse(list)") + .openBlock("list.map do |value|") + .call(() -> { + Shape memberTarget = + model.expectShape(s.getMember().getTarget()); + memberTarget + .accept(new MemberDeserializer(s.getMember(), "", "value", !s.hasTrait(SparseTrait.class))); + }) + .closeBlock("end") + .closeBlock("end"); } @Override protected void renderStructureParseMethod(StructureShape s) { + writer + .openBlock("def self.parse(map)") + .write("data = $T.new", context.symbolProvider().toSymbol(s)) + .call(() -> renderMemberParsers(s)) + .write("return data") + .closeBlock("end"); + } + @Override + protected void renderOperationParseMethod(OperationShape operation, Shape outputShape) { + Optional httpPayloadMember = outputShape.members() + .stream() + .filter((m) -> m.hasTrait(HttpPayloadTrait.class)) + .findFirst(); + + writer + .openBlock("def self.parse(http_resp)") + .write("data = $T.new", context.symbolProvider().toSymbol(outputShape)) + .call(() -> { + if (httpPayloadMember.isPresent()) { + if (Streaming.isStreaming(model, outputShape)) { + renderStreamingBodyParser(outputShape); + } else { + String memberName = symbolProvider.toMemberName(httpPayloadMember.get()); + writer.write("data.$L = http_resp.body.read", memberName); + } + } else { + writer + .write("body = http_resp.body.read") + .write("return data if body.empty?") + .write("map = $T.parse(body)", Hearth.JSON) + .call(() -> renderMemberParsers(outputShape)); + } + }) + .write("data") + .closeBlock("end"); } -} + @Override + protected void renderErrorParseMethod(Shape shape) { + writer + .openBlock("def self.parse(http_resp)") + .write("data = $T.new", context.symbolProvider().toSymbol(shape)) + .write("body = http_resp.body.read") + .write("return data if body.empty?") + .write("map = $T.parse(body)", Hearth.JSON); + renderMemberParsers(shape); + writer + .write("data") + .closeBlock("end"); + } + + private class MemberDeserializer extends ShapeVisitor.Default { + + private final String jsonGetter; + private final String dataSetter; + private final MemberShape memberShape; + private final boolean checkRequired; + + MemberDeserializer(MemberShape memberShape, + String dataSetter, String jsonGetter, boolean checkRequired) { + this.jsonGetter = jsonGetter; + this.dataSetter = dataSetter; + this.memberShape = memberShape; + this.checkRequired = checkRequired; + } + + private String checkRequired() { + if (this.checkRequired) { + return " unless " + jsonGetter + ".nil?"; + } else { + return ""; + } + } + + /** + * For simple shapes, just copy to the data. + */ + @Override + protected Void getDefault(Shape shape) { + writer.write("$L$L$L", dataSetter, jsonGetter, checkRequired()); + return null; + } + + private void rubyFloat() { + writer.write("$L$T.deserialize($L)$L", + dataSetter, Hearth.NUMBER_HELPER, jsonGetter, checkRequired()); + } + + @Override + public Void doubleShape(DoubleShape shape) { + rubyFloat(); + return null; + } + + @Override + public Void floatShape(FloatShape shape) { + rubyFloat(); + return null; + } + + @Override + public Void blobShape(BlobShape shape) { + writer.write("$1L$3T::decode64($2L) unless $2L.nil?", + dataSetter, jsonGetter, RubyImportContainer.BASE64); + return null; + } + + @Override + public Void timestampShape(TimestampShape shape) { + writer.write("$L$L if $L", dataSetter, + TimestampFormat.parseTimestamp( + shape, memberShape, jsonGetter, TimestampFormatTrait.Format.EPOCH_SECONDS), + jsonGetter); + return null; + } + + /** + * For complex shapes, simply delegate to their builder. + */ + private void defaultComplexDeserializer(Shape shape) { + if (checkRequired) { + writer.write("$1L$2L.parse($3L) unless $3L.nil?", + dataSetter, symbolProvider.toSymbol(shape).getName(), + jsonGetter); + } else { + writer.write("$1L($2L.parse($3L) unless $3L.nil?)", + dataSetter, symbolProvider.toSymbol(shape).getName(), + jsonGetter); + } + } + + @Override + public Void listShape(ListShape shape) { + defaultComplexDeserializer(shape); + return null; + } + + @Override + public Void mapShape(MapShape shape) { + defaultComplexDeserializer(shape); + return null; + } + + @Override + public Void structureShape(StructureShape shape) { + defaultComplexDeserializer(shape); + return null; + } + + @Override + public Void unionShape(UnionShape shape) { + defaultComplexDeserializer(shape); + return null; + } + } +} diff --git a/codegen/smithy-ruby-codegen-test-utils/src/main/java/software/amazon/smithy/ruby/codegen/protocol/generators/StubsGenerator.java b/codegen/smithy-ruby-codegen-test-utils/src/main/java/software/amazon/smithy/ruby/codegen/protocol/generators/StubsGenerator.java index 2e89e5ac3..82c536a33 100644 --- a/codegen/smithy-ruby-codegen-test-utils/src/main/java/software/amazon/smithy/ruby/codegen/protocol/generators/StubsGenerator.java +++ b/codegen/smithy-ruby-codegen-test-utils/src/main/java/software/amazon/smithy/ruby/codegen/protocol/generators/StubsGenerator.java @@ -15,19 +15,37 @@ package software.amazon.smithy.ruby.codegen.protocol.generators; +import java.util.Optional; +import java.util.stream.Stream; +import software.amazon.smithy.model.shapes.BlobShape; +import software.amazon.smithy.model.shapes.DoubleShape; +import software.amazon.smithy.model.shapes.FloatShape; import software.amazon.smithy.model.shapes.ListShape; import software.amazon.smithy.model.shapes.MapShape; import software.amazon.smithy.model.shapes.MemberShape; import software.amazon.smithy.model.shapes.OperationShape; import software.amazon.smithy.model.shapes.Shape; +import software.amazon.smithy.model.shapes.ShapeVisitor; import software.amazon.smithy.model.shapes.StructureShape; +import software.amazon.smithy.model.shapes.TimestampShape; import software.amazon.smithy.model.shapes.UnionShape; +import software.amazon.smithy.model.traits.ErrorTrait; +import software.amazon.smithy.model.traits.HttpErrorTrait; +import software.amazon.smithy.model.traits.HttpPayloadTrait; import software.amazon.smithy.model.traits.HttpTrait; +import software.amazon.smithy.model.traits.SparseTrait; import software.amazon.smithy.model.traits.StreamingTrait; +import software.amazon.smithy.model.traits.TimestampFormatTrait; import software.amazon.smithy.ruby.codegen.GenerationContext; +import software.amazon.smithy.ruby.codegen.Hearth; +import software.amazon.smithy.ruby.codegen.RubyImportContainer; import software.amazon.smithy.ruby.codegen.generators.RestStubsGeneratorBase; +import software.amazon.smithy.ruby.codegen.generators.StubsGeneratorBase; +import software.amazon.smithy.ruby.codegen.traits.NoSerializeTrait; +import software.amazon.smithy.ruby.codegen.util.Streaming; +import software.amazon.smithy.ruby.codegen.util.TimestampFormat; -public class StubsGenerator extends RestStubsGeneratorBase { +public class StubsGenerator extends StubsGeneratorBase { public StubsGenerator(GenerationContext context) { super(context); @@ -35,45 +53,254 @@ public StubsGenerator(GenerationContext context) { @Override protected void renderUnionStubMethod(UnionShape shape) { + writer + .openBlock("def self.stub(stub)") + .write("data = {}") + .write("case stub"); + shape.members().forEach((member) -> { + writer + .write("when $T", context.symbolProvider().toSymbol(member)) + .indent(); + renderUnionMemberStubber(shape, member); + writer.dedent(); + }); + writer.openBlock("else") + .write("raise ArgumentError,\n\"Expected input to be one of the subclasses of $T\"", + context.symbolProvider().toSymbol(shape)) + .closeBlock("end") + .write("") + .write("data") + .closeBlock("end"); + } + + private void renderUnionMemberStubber(UnionShape shape, MemberShape member) { + Shape target = model.expectShape(member.getTarget()); + String symbolName = "'" + member.getMemberName() + "'"; + String dataSetter = "data[" + symbolName + "] = "; + target.accept(new MemberSerializer(member, dataSetter, "stub.__getobj__", false)); } @Override protected void renderListStubMethod(ListShape shape) { - + writer + .openBlock("def self.stub(stub)") + .write("stub ||= []") + .write("data = []") + .openBlock("stub.each do |element|") + .call(() -> { + Shape memberTarget = + model.expectShape(shape.getMember().getTarget()); + memberTarget + .accept(new MemberSerializer(shape.getMember(), "data << ", "element", + !shape.hasTrait(SparseTrait.class))); + }) + .closeBlock("end") + .write("data") + .closeBlock("end"); } @Override protected void renderMapStubMethod(MapShape shape) { - + writer + .openBlock("def self.stub(stub)") + .write("stub ||= {}") + .write("data = {}") + .openBlock("stub.each do |key, value|") + .call(() -> { + Shape valueTarget = model.expectShape(shape.getValue().getTarget()); + valueTarget + .accept(new MemberSerializer(shape.getValue(), "data[key] = ", "value", + !shape.hasTrait(SparseTrait.class))); + }) + .closeBlock("end") + .write("data") + .closeBlock("end"); } @Override protected void renderStructureStubMethod(StructureShape shape) { - + writer + .openBlock("def self.stub(stub)") + .write("stub ||= $T.new", context.symbolProvider().toSymbol(shape)) + .write("data = {}") + .call(() -> renderMemberStubbers(shape)) + .write("data") + .closeBlock("end"); } @Override - protected void renderPayloadBodyStub(Shape outputShape, MemberShape payloadMember, Shape target) { - if (target.hasTrait(StreamingTrait.class)) { - renderStreamingStub(outputShape); - } - } + protected void renderOperationStubMethod(OperationShape operation, Shape outputShape) { + Optional httpPayloadMember = outputShape.members() + .stream() + .filter((m) -> m.hasTrait(HttpPayloadTrait.class)) + .findFirst(); - @Override - protected void renderBodyStub(Shape outputShape) { + writer + .openBlock("def self.stub(http_resp, stub:)") + .call(() -> { + if (httpPayloadMember.isPresent()) { + String memberName = symbolProvider.toMemberName(httpPayloadMember.get()); + if (Streaming.isStreaming(model, outputShape)) { + writer.write("IO.copy_stream(stub.$L, http_resp.body)", + memberName); + } else { + writer.write("http_resp.body.write(stub.$L || '')", memberName); + } + } else { + writer.write("data = {}"); + renderMemberStubbers(outputShape); + writer.write("http_resp.body = $T.new($T.dump(data))", + RubyImportContainer.STRING_IO, Hearth.JSON); + } + }) + .write("http_resp.status = 200") + .closeBlock("end"); } @Override protected void renderErrorStubMethod(Shape errorShape) { + writer + .openBlock("def self.stub(http_resp, stub:)") + .call(() -> renderStatusCodeStubber(errorShape)) + .write("data = {}") + .write("data['__type'] = '$L'", errorShape.toShapeId()) + .call(() -> renderMemberStubbers(errorShape)) + .write("http_resp.body = $T.new($T.dump(data))", RubyImportContainer.STRING_IO, Hearth.JSON) + .closeBlock("end"); + } + + protected void renderStatusCodeStubber(Shape errorShape) { + String statusCode = ""; + Optional optionalHttpErrorTrait = errorShape.getTrait(HttpErrorTrait.class); + if (optionalHttpErrorTrait.isPresent()) { + statusCode = Integer.toString(((HttpErrorTrait) optionalHttpErrorTrait.get()).getCode()); + } else { + ErrorTrait errorTrait = (ErrorTrait) errorShape.getTrait(ErrorTrait.class).get(); + if (errorTrait.isClientError()) { + statusCode = "400"; + } else if (errorTrait.isServerError()) { + statusCode = "500"; + } + } + this.writer.write("http_resp.status = $1L", new Object[] {statusCode}); } - @Override - protected void renderStatusCodeStubber(OperationShape operation) { - writer.write("http_resp.status = 200"); + private void renderMemberStubbers(Shape s) { + //remove members w/ http traits or marked NoSerialize + Stream serializeMembers = s.members().stream() + .filter(NoSerializeTrait.excludeNoSerializeMembers()); + + serializeMembers.forEach((member) -> { + Shape target = model.expectShape(member.getTarget()); + String dataName = "'" + member.getMemberName() + "'"; + String dataSetter = "data[" + dataName + "] = "; + String inputGetter = "stub." + symbolProvider.toMemberName(member); + target.accept(new MemberSerializer(member, dataSetter, inputGetter, true)); + }); } -} + private class MemberSerializer extends ShapeVisitor.Default { + + private final MemberShape memberShape; + private final String inputGetter; + private final String dataSetter; + private final boolean checkRequired; + + + MemberSerializer(MemberShape memberShape, + String dataSetter, String inputGetter, boolean checkRequired) { + this.memberShape = memberShape; + this.inputGetter = inputGetter; + this.dataSetter = dataSetter; + this.checkRequired = checkRequired; + } + + private String checkRequired() { + if (this.checkRequired) { + return " unless " + inputGetter + ".nil?"; + } else { + return ""; + } + } + + @Override + protected Void getDefault(Shape shape) { + writer.write("$L$L$L", dataSetter, inputGetter, checkRequired()); + return null; + } + private void rubyFloat() { + writer.write("$L$T.serialize($L)", + dataSetter, Hearth.NUMBER_HELPER, inputGetter); + } + + @Override + public Void doubleShape(DoubleShape shape) { + rubyFloat(); + return null; + } + + @Override + public Void floatShape(FloatShape shape) { + rubyFloat(); + return null; + } + + @Override + public Void blobShape(BlobShape shape) { + writer.write("$L$T::strict_encode64($L)$L", + dataSetter, RubyImportContainer.BASE64, inputGetter, checkRequired()); + return null; + } + + @Override + public Void timestampShape(TimestampShape shape) { + writer.write("$L$L$L", + dataSetter, + TimestampFormat.serializeTimestamp( + shape, memberShape, inputGetter, TimestampFormatTrait.Format.EPOCH_SECONDS, false), + checkRequired()); + return null; + } + + /** + * For complex shapes, simply delegate to their Stubber. + */ + private void defaultComplexSerializer(Shape shape) { + if (checkRequired) { + writer.write("$1L$2L.stub($3L) unless $3L.nil?", dataSetter, + symbolProvider.toSymbol(shape).getName(), inputGetter); + } else { + writer.write("$1L($2L.stub($3L) unless $3L.nil?)", dataSetter, + symbolProvider.toSymbol(shape).getName(), inputGetter); + } + } + + @Override + public Void listShape(ListShape shape) { + defaultComplexSerializer(shape); + return null; + } + + @Override + public Void mapShape(MapShape shape) { + defaultComplexSerializer(shape); + return null; + } + + @Override + public Void structureShape(StructureShape shape) { + defaultComplexSerializer(shape); + return null; + } + + @Override + public Void unionShape(UnionShape shape) { + defaultComplexSerializer(shape); + return null; + } + } +} diff --git a/codegen/smithy-ruby-codegen-test-utils/src/main/java/software/amazon/smithy/ruby/codegen/transformer/EndpointTestServiceTransformer.java b/codegen/smithy-ruby-codegen-test-utils/src/main/java/software/amazon/smithy/ruby/codegen/transformer/EndpointTestServiceTransformer.java index d976a29db..fc73bd509 100644 --- a/codegen/smithy-ruby-codegen-test-utils/src/main/java/software/amazon/smithy/ruby/codegen/transformer/EndpointTestServiceTransformer.java +++ b/codegen/smithy-ruby-codegen-test-utils/src/main/java/software/amazon/smithy/ruby/codegen/transformer/EndpointTestServiceTransformer.java @@ -4,7 +4,7 @@ import software.amazon.smithy.build.TransformContext; import software.amazon.smithy.model.Model; import software.amazon.smithy.model.transform.ModelTransformer; -import software.amazon.smithy.ruby.codegen.traits.FakeProtocolTrait; +import software.amazon.smithy.ruby.codegen.traits.TestProtocolTrait; /** * A transformer to ensure we can generate an SDK to test Rules-Engine test cases @@ -22,7 +22,7 @@ public Model transform(TransformContext context) { return transformer.mapShapes(model, (shape) -> { if (shape.isServiceShape()) { - return shape.asServiceShape().get().toBuilder().addTrait(new FakeProtocolTrait()).build(); + return shape.asServiceShape().get().toBuilder().addTrait(new TestProtocolTrait()).build(); } else { return shape; } diff --git a/codegen/smithy-ruby-codegen-test-utils/src/main/resources/META-INF/services/software.amazon.smithy.ruby.codegen.RubyIntegration b/codegen/smithy-ruby-codegen-test-utils/src/main/resources/META-INF/services/software.amazon.smithy.ruby.codegen.RubyIntegration index 3d3a2b249..8691a4fc8 100644 --- a/codegen/smithy-ruby-codegen-test-utils/src/main/resources/META-INF/services/software.amazon.smithy.ruby.codegen.RubyIntegration +++ b/codegen/smithy-ruby-codegen-test-utils/src/main/resources/META-INF/services/software.amazon.smithy.ruby.codegen.RubyIntegration @@ -1,2 +1,2 @@ -software.amazon.smithy.ruby.codegen.integrations.FakeProtocolIntegration +software.amazon.smithy.ruby.codegen.integrations.TestProtocolIntegration software.amazon.smithy.ruby.codegen.integrations.WhiteLabelTestIntegration diff --git a/codegen/smithy-ruby-codegen-test/integration-specs/endpoints_spec.rb b/codegen/smithy-ruby-codegen-test/integration-specs/endpoints_spec.rb index e969ff3e2..efa6936cd 100644 --- a/codegen/smithy-ruby-codegen-test/integration-specs/endpoints_spec.rb +++ b/codegen/smithy-ruby-codegen-test/integration-specs/endpoints_spec.rb @@ -46,7 +46,7 @@ module WhiteLabel it 'prepends the label to the host' do proc = proc do |context| expect(context.request.uri.to_s) - .to eq("https://foo.#{label}.data.whitelabel.com") + .to eq("https://foo.#{label}.data.whitelabel.com/") end interceptor = Hearth::Interceptor.new(read_before_transmit: proc) client.endpoint_with_host_label_operation( diff --git a/codegen/smithy-ruby-codegen-test/model/component-test/main.smithy b/codegen/smithy-ruby-codegen-test/model/component-test/main.smithy index de5a905bd..d4e771872 100644 --- a/codegen/smithy-ruby-codegen-test/model/component-test/main.smithy +++ b/codegen/smithy-ruby-codegen-test/model/component-test/main.smithy @@ -1,7 +1,7 @@ $version: "2.0" namespace smithy.ruby.tests -use smithy.ruby.tests.protocols#fakeProtocol +use smithy.ruby.tests.protocols#testProtocol use smithy.rules#clientContextParams use smithy.rules#endpointRuleSet use smithy.rules#endpointTests @@ -264,8 +264,8 @@ testCases: [ documentation: "Specify the stage (beta|gamma|prod)" } ) -@fakeProtocol -@title("FakeProtocol Test Service") +@testProtocol +@title("TestProtocol Test Service") service WhiteLabel { version: "2018-01-01", operations: [ diff --git a/codegen/smithy-ruby-codegen-test/model/fake-protocol.smithy b/codegen/smithy-ruby-codegen-test/model/test-protocol.smithy similarity index 83% rename from codegen/smithy-ruby-codegen-test/model/fake-protocol.smithy rename to codegen/smithy-ruby-codegen-test/model/test-protocol.smithy index d83131a07..0854e3484 100644 --- a/codegen/smithy-ruby-codegen-test/model/fake-protocol.smithy +++ b/codegen/smithy-ruby-codegen-test/model/test-protocol.smithy @@ -4,4 +4,4 @@ namespace smithy.ruby.tests.protocols // Define a fake protocol trait for use. @trait @protocolDefinition -structure fakeProtocol {} \ No newline at end of file +structure testProtocol {} \ No newline at end of file diff --git a/codegen/smithy-ruby-codegen/src/main/java/software/amazon/smithy/ruby/codegen/generators/BuilderGeneratorBase.java b/codegen/smithy-ruby-codegen/src/main/java/software/amazon/smithy/ruby/codegen/generators/BuilderGeneratorBase.java index 9fd68767a..beb5ba5d8 100644 --- a/codegen/smithy-ruby-codegen/src/main/java/software/amazon/smithy/ruby/codegen/generators/BuilderGeneratorBase.java +++ b/codegen/smithy-ruby-codegen/src/main/java/software/amazon/smithy/ruby/codegen/generators/BuilderGeneratorBase.java @@ -288,7 +288,6 @@ protected void renderBuildersForOperation(OperationShape operation, Shape inputS /** * @param inputShape inputShape from a streaming operation to render for. */ - // TODO: should probably be in Rest Base protected void renderStreamingBodyBuilder(Shape inputShape) { MemberShape streamingMember = inputShape.members().stream() .filter((m) -> m.getMemberTrait(model, StreamingTrait.class).isPresent()) diff --git a/codegen/smithy-ruby-codegen/src/main/java/software/amazon/smithy/ruby/codegen/traits/FakeProtocolTrait.java b/codegen/smithy-ruby-codegen/src/main/java/software/amazon/smithy/ruby/codegen/traits/TestProtocolTrait.java similarity index 76% rename from codegen/smithy-ruby-codegen/src/main/java/software/amazon/smithy/ruby/codegen/traits/FakeProtocolTrait.java rename to codegen/smithy-ruby-codegen/src/main/java/software/amazon/smithy/ruby/codegen/traits/TestProtocolTrait.java index 8cdb32414..752396e94 100644 --- a/codegen/smithy-ruby-codegen/src/main/java/software/amazon/smithy/ruby/codegen/traits/FakeProtocolTrait.java +++ b/codegen/smithy-ruby-codegen/src/main/java/software/amazon/smithy/ruby/codegen/traits/TestProtocolTrait.java @@ -20,23 +20,23 @@ import software.amazon.smithy.model.shapes.ShapeId; import software.amazon.smithy.model.traits.AnnotationTrait; -public final class FakeProtocolTrait extends AnnotationTrait { +public final class TestProtocolTrait extends AnnotationTrait { public static final ShapeId ID = - ShapeId.from("smithy.ruby.tests.protocols#fakeProtocol"); + ShapeId.from("smithy.ruby.tests.protocols#testProtocol"); - public FakeProtocolTrait() { + public TestProtocolTrait() { this(Node.objectNode()); } - public FakeProtocolTrait(ObjectNode node) { + public TestProtocolTrait(ObjectNode node) { super(ID, node); } public static final class Provider - extends AnnotationTrait.Provider { + extends AnnotationTrait.Provider { public Provider() { - super(ID, FakeProtocolTrait::new); + super(ID, TestProtocolTrait::new); } } } diff --git a/codegen/smithy-ruby-codegen/src/main/resources/META-INF/services/software.amazon.smithy.model.traits.TraitService b/codegen/smithy-ruby-codegen/src/main/resources/META-INF/services/software.amazon.smithy.model.traits.TraitService index fed45631d..9f9ac704b 100644 --- a/codegen/smithy-ruby-codegen/src/main/resources/META-INF/services/software.amazon.smithy.model.traits.TraitService +++ b/codegen/smithy-ruby-codegen/src/main/resources/META-INF/services/software.amazon.smithy.model.traits.TraitService @@ -1,2 +1,2 @@ software.amazon.smithy.ruby.codegen.traits.SkipTestsTrait$Provider -software.amazon.smithy.ruby.codegen.traits.FakeProtocolTrait$Provider +software.amazon.smithy.ruby.codegen.traits.TestProtocolTrait$Provider