Skip to content
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

Default empty array for previously flattened lists and maps in query compatible services #2948

Merged
merged 12 commits into from
Nov 20, 2023
2 changes: 2 additions & 0 deletions gems/aws-sdk-core/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
Unreleased Changes
------------------

* Issue - For `awsQueryCompatible` services, default an empty list or map for shapes that were previously flattened in the query protocol.

3.187.0 (2023-11-17)
------------------

Expand Down
9 changes: 8 additions & 1 deletion gems/aws-sdk-core/lib/aws-sdk-core/json/handler.rb
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,10 @@ def parse_body(context)
end
resp_struct
else
Parser.new(rules).parse(json == '' ? '{}' : json)
Parser.new(
rules,
query_compatible: query_compatible?(context)
).parse(json == '' ? '{}' : json)
end
else
EmptyStructure.new
Expand All @@ -83,6 +86,10 @@ def simple_json?(context)
context.config.simple_json
end

def query_compatible?(context)
context.config.api.metadata.key?('awsQueryCompatible')
end

end
end
end
27 changes: 26 additions & 1 deletion gems/aws-sdk-core/lib/aws-sdk-core/json/parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ class Parser
include Seahorse::Model::Shapes

# @param [Seahorse::Model::ShapeRef] rules
def initialize(rules)
def initialize(rules, query_compatible: false)
@rules = rules
@query_compatible = query_compatible
end

# @param [String<JSON>] json
Expand All @@ -32,6 +33,22 @@ def structure(ref, values, target = nil)
target[:unknown] = { 'name' => key, 'value' => value }
end
end
# In services that were previously Query/XML, members that were
# "flattened" defaulted to empty lists. In JSON, these values are nil,
# which is backwards incompatible. To preserve backwards compatibility,
# we set a default value of [] for these members.
if @query_compatible
ref.shape.members.each do |member_name, member_target|
next unless target[member_name].nil?

if flattened_list?(member_target.shape)
target[member_name] = []
elsif flattened_map?(member_target.shape)
target[member_name] = {}
end
end
end

if shape.union
# convert to subclass
member_subclass = shape.member_subclass(target.member).new
Expand Down Expand Up @@ -79,6 +96,14 @@ def time(value)
value.is_a?(Numeric) ? Time.at(value) : Time.parse(value)
end

def flattened_list?(shape)
shape.is_a?(ListShape) && shape.flattened
end

def flattened_map?(shape)
shape.is_a?(MapShape) && shape.flattened
end

end
end
end
126 changes: 38 additions & 88 deletions gems/aws-sdk-sqs/spec/client/verify_checksums_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -54,25 +54,13 @@ class Client
let(:md5_of_attributes) { '756d7f4338696745d063b420a2f7e502' }

before(:each) do
if client.config.api.metadata['protocol'] == 'json'
response_body = <<-JSON
{
"MD5OfMessageAttributes": "#{md5_of_attributes}",
"MD5OfMessageBody": "900150983cd24fb0d6963f7d28e17f72",
"MessageId": "5fea7756-0ea4-451a-a703-a558b933e274"
}
JSON
else
response_body = <<-XML
<SendMessageResponse>
<SendMessageResult>
<MD5OfMessageBody>900150983cd24fb0d6963f7d28e17f72</MD5OfMessageBody>
<MD5OfMessageAttributes>#{md5_of_attributes}</MD5OfMessageAttributes>
<MessageId>5fea7756-0ea4-451a-a703-a558b933e274</MessageId>
</SendMessageResult>
</SendMessageResponse>
XML
end
response_body = <<-JSON
{
"MD5OfMessageAttributes": "#{md5_of_attributes}",
"MD5OfMessageBody": "900150983cd24fb0d6963f7d28e17f72",
"MessageId": "5fea7756-0ea4-451a-a703-a558b933e274"
}
JSON

client.handle(step: :send) do |context|
context.http_response.signal_done(
Expand Down Expand Up @@ -167,42 +155,22 @@ class Client
describe '#send_message_batch' do

before(:each) do
if client.config.api.metadata['protocol'] == 'json'
client.handle(step: :send) do |context|
context.http_response.signal_done(
status_code: 200,
headers: {},
body:<<-JSON)
{
"Successful": [
{
"Id": "msg-id",
"MD5OfMessageBody": "900150983cd24fb0d6963f7d28e17f72",
"MD5OfMessageAttributes": "756d7f4338696745d063b420a2f7e502"
}
]
}
JSON
Seahorse::Client::Response.new(context: context)
end
else
client.handle(step: :send) do |context|
context.http_response.signal_done(
status_code: 200,
headers: {},
body:<<-XML)
<SendMessageBatchResponse>
<SendMessageBatchResult>
<SendMessageBatchResultEntry>
<Id>msg-id</Id>
<MD5OfMessageBody>900150983cd24fb0d6963f7d28e17f72</MD5OfMessageBody>
<MD5OfMessageAttributes>756d7f4338696745d063b420a2f7e502</MD5OfMessageAttributes>
</SendMessageBatchResultEntry>
</SendMessageBatchResult>
</SendMessageBatchResponse>
XML
Seahorse::Client::Response.new(context: context)
end
client.handle(step: :send) do |context|
context.http_response.signal_done(
status_code: 200,
headers: {},
body:<<-JSON)
{
"Successful": [
{
"Id": "msg-id",
"MD5OfMessageBody": "900150983cd24fb0d6963f7d28e17f72",
"MD5OfMessageAttributes": "756d7f4338696745d063b420a2f7e502"
}
]
}
JSON
Seahorse::Client::Response.new(context: context)
end
end

Expand Down Expand Up @@ -253,39 +221,21 @@ class Client
end

it 'skips failed errors' do
if client.config.api.metadata['protocol'] == 'json'
client.handle(step: :send) do |context|
context.http_response.signal_done(
status_code: 200,
headers: {},
body:<<-JSON)
{
"Successful": [],
"Failed": [
{
"Id": "msg-id"
}
]
}
JSON
Seahorse::Client::Response.new(context: context)
end
else
client.handle(step: :send) do |context|
context.http_response.signal_done(
status_code: 200,
headers: {},
body:<<-XML)
<SendMessageBatchResponse>
<SendMessageBatchResult>
<BatchResultErrorEntry>
<Id>msg-id</Id>
</BatchResultErrorEntry>
</SendMessageBatchResult>
</SendMessageBatchResponse>
XML
Seahorse::Client::Response.new(context: context)
end
client.handle(step: :send) do |context|
context.http_response.signal_done(
status_code: 200,
headers: {},
body:<<-JSON)
{
"Successful": [],
"Failed": [
{
"Id": "msg-id"
}
]
}
JSON
Seahorse::Client::Response.new(context: context)
end
expect {
client.send_message_batch(
Expand Down
47 changes: 22 additions & 25 deletions gems/aws-sdk-sqs/spec/client_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,36 +21,33 @@ module SQS
end

describe 'empty result element' do
it 'returns a structure with all of the root members' do
if client.config.api.metadata['protocol'] == 'json'
client.handle(step: :send) do |context|
context.http_response.signal_done(
status_code: 200,
headers: {},
body: '{"Messages": []}'
)
Seahorse::Client::Response.new(context: context)
end
else
client.handle(step: :send) do |context|
context.http_response.signal_done(
status_code: 200,
headers: {},
body:<<-XML)
<ReceiveMessageResponse>
<ReceiveMessageResult/>
<ResponseMetadata>
<RequestId>request-id</RequestId>
</ResponseMetadata>
</ReceiveMessageResponse>
XML
Seahorse::Client::Response.new(context: context)
end
it 'defaults to an empty list' do
client.handle(step: :send) do |context|
context.http_response.signal_done(
status_code: 200,
headers: {},
body: '{}'
)
Seahorse::Client::Response.new(context: context)
end
resp = client.receive_message(queue_url: 'https://foo.com')
expect(resp.data.members).to eq([:messages])
expect(resp.data.messages).to eq([])
end

it 'defaults to an empty map' do
client.handle(step: :send) do |context|
context.http_response.signal_done(
status_code: 200,
headers: {},
body: '{}'
)
Seahorse::Client::Response.new(context: context)
end
resp = client.list_queue_tags(queue_url: 'https://foo.com')
expect(resp.data.members).to eq([:tags])
expect(resp.data.tags).to eq({})
end
end

describe '#stub_responses' do
Expand Down
Loading