Skip to content

Commit a48c009

Browse files
authored
Use standardized REST-JSON behavior (#2783)
* Add REST-JSON Content-Type tests (#2756) Add SDK standard test cases that verify correct conte-type handling for the REST-JSON protocol. * Follow standard REST-JSON content-type handling (#2765) * Follow standard REST-JSON content-type handling Update the REST-JSON marshalling logic to conform to the standard expected behavior WRT to the `Content-Type` of the request. * Add additional protocol test Verifies that when there are unbound members (no `location` trait), and they are null, that we continue to marshall to an empty JSON object in the body. * Extract block to method * Don't create object if no implicit members (#2773) This fixes an edge case in the REST-JSON marshaller: if the request has neither an explicit payload nor implicit payload members (i.e. members not explicitly bound to an location), then the marshaller should not create an create an object. i.e. the request body should be "", and not "{}".
1 parent fe07efb commit a48c009

File tree

19 files changed

+630
-67
lines changed

19 files changed

+630
-67
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"category": "AWS SDK for Java v2",
3+
"contributor": "",
4+
"type": "bugfix",
5+
"description": "Update the REST-JSON marshalling logic to conform to the standard expected behavior WRT to the `Content-Type` of the request."
6+
}

codegen/src/main/java/software/amazon/awssdk/codegen/model/intermediate/ShapeModel.java

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -187,11 +187,17 @@ public List<MemberModel> getUnboundMembers() {
187187
List<MemberModel> unboundMembers = new ArrayList<>();
188188
if (members != null) {
189189
for (MemberModel member : members) {
190-
if (member.getHttp().getLocation() == null) {
190+
if (member.getHttp().getLocation() == null && !member.getHttp().getIsPayload()) {
191191
if (hasPayloadMember) {
192+
// There is an explicit payload, but this unbound
193+
// member isn't it.
194+
// Note: Somewhat unintuitive, explicit payloads don't
195+
// have an explicit location; they're identified by
196+
// the payload HTTP trait being true.
192197
throw new IllegalStateException(String.format(
193-
"C2J Shape %s has both an explicit payload member and unbound (no explicit location) members. "
194-
+ "This is undefined behavior, verify the correctness of the C2J model", c2jName));
198+
"C2J Shape %s has both an explicit payload member and unbound (no explicit location) member, %s."
199+
+ " This is undefined behavior, verify the correctness of the C2J model.",
200+
c2jName, member.getName()));
195201
}
196202
unboundMembers.add(member);
197203
}
@@ -221,7 +227,12 @@ public List<MemberModel> getUnboundEventMembers() {
221227
public boolean hasPayloadMembers() {
222228
return hasPayloadMember ||
223229
getExplicitEventPayloadMember() != null ||
224-
!getUnboundMembers().isEmpty() ||
230+
hasImplicitPayloadMembers();
231+
232+
}
233+
234+
public boolean hasImplicitPayloadMembers() {
235+
return !getUnboundMembers().isEmpty() ||
225236
(isEvent() && !getUnboundEventMembers().isEmpty());
226237
}
227238

codegen/src/main/java/software/amazon/awssdk/codegen/poet/transform/protocols/JsonMarshallerSpec.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ protected FieldSpec operationInfoField() {
9595
.add(".httpMethod($T.$L)", SdkHttpMethod.class, shapeModel.getMarshaller().getVerb())
9696
.add(".hasExplicitPayloadMember($L)", shapeModel.isHasPayloadMember() ||
9797
shapeModel.getExplicitEventPayloadMember() != null)
98+
.add(".hasImplicitPayloadMembers($L)", shapeModel.hasImplicitPayloadMembers())
9899
.add(".hasPayloadMembers($L)", shapeModel.hasPayloadMembers());
99100

100101
if (StringUtils.isNotBlank(shapeModel.getMarshaller().getTarget())) {

codegen/src/test/resources/software/amazon/awssdk/codegen/poet/transform/alltypesrequestmarshaller.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@
1919
@SdkInternalApi
2020
public class AllTypesRequestMarshaller implements Marshaller<AllTypesRequest> {
2121
private static final OperationInfo SDK_OPERATION_BINDING = OperationInfo.builder().requestUri("/")
22-
.httpMethod(SdkHttpMethod.POST).hasExplicitPayloadMember(false).hasPayloadMembers(true).build();
22+
.httpMethod(SdkHttpMethod.POST).hasExplicitPayloadMember(false).hasImplicitPayloadMembers(true)
23+
.hasPayloadMembers(true).build();
2324

2425
private final BaseAwsJsonProtocolFactory protocolFactory;
2526

@@ -32,11 +33,10 @@ public SdkHttpFullRequest marshall(AllTypesRequest allTypesRequest) {
3233
Validate.paramNotNull(allTypesRequest, "allTypesRequest");
3334
try {
3435
ProtocolMarshaller<SdkHttpFullRequest> protocolMarshaller = protocolFactory
35-
.createProtocolMarshaller(SDK_OPERATION_BINDING);
36+
.createProtocolMarshaller(SDK_OPERATION_BINDING);
3637
return protocolMarshaller.marshall(allTypesRequest);
3738
} catch (Exception e) {
3839
throw SdkClientException.builder().message("Unable to marshall request to JSON: " + e.getMessage()).cause(e).build();
3940
}
4041
}
4142
}
42-

codegen/src/test/resources/software/amazon/awssdk/codegen/poet/transform/eventstreamoperationrequestmarshaller.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@
1919
@SdkInternalApi
2020
public class EventStreamOperationRequestMarshaller implements Marshaller<EventStreamOperationRequest> {
2121
private static final OperationInfo SDK_OPERATION_BINDING = OperationInfo.builder()
22-
.requestUri("/2016-03-11/eventStreamOperation").httpMethod(SdkHttpMethod.POST).hasExplicitPayloadMember(true)
23-
.hasPayloadMembers(true).hasEventStreamingInput(true).build();
22+
.requestUri("/2016-03-11/eventStreamOperation").httpMethod(SdkHttpMethod.POST).hasExplicitPayloadMember(true)
23+
.hasImplicitPayloadMembers(false).hasPayloadMembers(true).hasEventStreamingInput(true).build();
2424

2525
private final BaseAwsJsonProtocolFactory protocolFactory;
2626

@@ -33,7 +33,7 @@ public SdkHttpFullRequest marshall(EventStreamOperationRequest eventStreamOperat
3333
Validate.paramNotNull(eventStreamOperationRequest, "eventStreamOperationRequest");
3434
try {
3535
ProtocolMarshaller<SdkHttpFullRequest> protocolMarshaller = protocolFactory
36-
.createProtocolMarshaller(SDK_OPERATION_BINDING);
36+
.createProtocolMarshaller(SDK_OPERATION_BINDING);
3737
return protocolMarshaller.marshall(eventStreamOperationRequest);
3838
} catch (Exception e) {
3939
throw SdkClientException.builder().message("Unable to marshall request to JSON: " + e.getMessage()).cause(e).build();

codegen/src/test/resources/software/amazon/awssdk/codegen/poet/transform/eventstreamoperationwithonlyinputrequestmarshaller.java

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@
1717
*/
1818
@Generated("software.amazon.awssdk:codegen")
1919
@SdkInternalApi
20-
public class EventStreamOperationWithOnlyInputRequestMarshaller implements
21-
Marshaller<EventStreamOperationWithOnlyInputRequest> {
20+
public class EventStreamOperationWithOnlyInputRequestMarshaller implements Marshaller<EventStreamOperationWithOnlyInputRequest> {
2221
private static final OperationInfo SDK_OPERATION_BINDING = OperationInfo.builder()
23-
.requestUri("/2016-03-11/EventStreamOperationWithOnlyInput").httpMethod(SdkHttpMethod.POST)
24-
.hasExplicitPayloadMember(false).hasPayloadMembers(true).hasEventStreamingInput(true).build();
22+
.requestUri("/2016-03-11/EventStreamOperationWithOnlyInput").httpMethod(SdkHttpMethod.POST)
23+
.hasExplicitPayloadMember(false).hasImplicitPayloadMembers(true).hasPayloadMembers(true).hasEventStreamingInput(true)
24+
.build();
2525

2626
private final BaseAwsJsonProtocolFactory protocolFactory;
2727

@@ -34,11 +34,10 @@ public SdkHttpFullRequest marshall(EventStreamOperationWithOnlyInputRequest even
3434
Validate.paramNotNull(eventStreamOperationWithOnlyInputRequest, "eventStreamOperationWithOnlyInputRequest");
3535
try {
3636
ProtocolMarshaller<SdkHttpFullRequest> protocolMarshaller = protocolFactory
37-
.createProtocolMarshaller(SDK_OPERATION_BINDING);
37+
.createProtocolMarshaller(SDK_OPERATION_BINDING);
3838
return protocolMarshaller.marshall(eventStreamOperationWithOnlyInputRequest);
3939
} catch (Exception e) {
4040
throw SdkClientException.builder().message("Unable to marshall request to JSON: " + e.getMessage()).cause(e).build();
4141
}
4242
}
4343
}
44-

codegen/src/test/resources/software/amazon/awssdk/codegen/poet/transform/nestedcontainersrequestmarshaller.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@
1919
@SdkInternalApi
2020
public class NestedContainersRequestMarshaller implements Marshaller<NestedContainersRequest> {
2121
private static final OperationInfo SDK_OPERATION_BINDING = OperationInfo.builder().requestUri("/")
22-
.httpMethod(SdkHttpMethod.POST).hasExplicitPayloadMember(false).hasPayloadMembers(true).build();
22+
.httpMethod(SdkHttpMethod.POST).hasExplicitPayloadMember(false).hasImplicitPayloadMembers(true)
23+
.hasPayloadMembers(true).build();
2324

2425
private final BaseAwsJsonProtocolFactory protocolFactory;
2526

@@ -32,11 +33,10 @@ public SdkHttpFullRequest marshall(NestedContainersRequest nestedContainersReque
3233
Validate.paramNotNull(nestedContainersRequest, "nestedContainersRequest");
3334
try {
3435
ProtocolMarshaller<SdkHttpFullRequest> protocolMarshaller = protocolFactory
35-
.createProtocolMarshaller(SDK_OPERATION_BINDING);
36+
.createProtocolMarshaller(SDK_OPERATION_BINDING);
3637
return protocolMarshaller.marshall(nestedContainersRequest);
3738
} catch (Exception e) {
3839
throw SdkClientException.builder().message("Unable to marshall request to JSON: " + e.getMessage()).cause(e).build();
3940
}
4041
}
4142
}
42-

codegen/src/test/resources/software/amazon/awssdk/codegen/poet/transform/operationwithnoinputoroutputrequestmarshaller.java

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,10 @@
1717
*/
1818
@Generated("software.amazon.awssdk:codegen")
1919
@SdkInternalApi
20-
public class OperationWithNoInputOrOutputRequestMarshaller implements
21-
Marshaller<OperationWithNoInputOrOutputRequest> {
20+
public class OperationWithNoInputOrOutputRequestMarshaller implements Marshaller<OperationWithNoInputOrOutputRequest> {
2221
private static final OperationInfo SDK_OPERATION_BINDING = OperationInfo.builder().requestUri("/")
23-
.httpMethod(SdkHttpMethod.POST).hasExplicitPayloadMember(false).hasPayloadMembers(false).build();
22+
.httpMethod(SdkHttpMethod.POST).hasExplicitPayloadMember(false).hasImplicitPayloadMembers(false)
23+
.hasPayloadMembers(false).build();
2424

2525
private final BaseAwsJsonProtocolFactory protocolFactory;
2626

@@ -33,11 +33,10 @@ public SdkHttpFullRequest marshall(OperationWithNoInputOrOutputRequest operation
3333
Validate.paramNotNull(operationWithNoInputOrOutputRequest, "operationWithNoInputOrOutputRequest");
3434
try {
3535
ProtocolMarshaller<SdkHttpFullRequest> protocolMarshaller = protocolFactory
36-
.createProtocolMarshaller(SDK_OPERATION_BINDING);
36+
.createProtocolMarshaller(SDK_OPERATION_BINDING);
3737
return protocolMarshaller.marshall(operationWithNoInputOrOutputRequest);
3838
} catch (Exception e) {
3939
throw SdkClientException.builder().message("Unable to marshall request to JSON: " + e.getMessage()).cause(e).build();
4040
}
4141
}
4242
}
43-

codegen/src/test/resources/software/amazon/awssdk/codegen/poet/transform/streaminginputoperationrequestmarshaller.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@
1919
@SdkInternalApi
2020
public class StreamingInputOperationRequestMarshaller implements Marshaller<StreamingInputOperationRequest> {
2121
private static final OperationInfo SDK_OPERATION_BINDING = OperationInfo.builder()
22-
.requestUri("/2016-03-11/streamingInputOperation").httpMethod(SdkHttpMethod.POST).hasExplicitPayloadMember(true)
23-
.hasPayloadMembers(true).hasStreamingInput(true).build();
22+
.requestUri("/2016-03-11/streamingInputOperation").httpMethod(SdkHttpMethod.POST).hasExplicitPayloadMember(true)
23+
.hasImplicitPayloadMembers(false).hasPayloadMembers(true).hasStreamingInput(true).build();
2424

2525
private final BaseAwsJsonProtocolFactory protocolFactory;
2626

@@ -33,11 +33,10 @@ public SdkHttpFullRequest marshall(StreamingInputOperationRequest streamingInput
3333
Validate.paramNotNull(streamingInputOperationRequest, "streamingInputOperationRequest");
3434
try {
3535
ProtocolMarshaller<SdkHttpFullRequest> protocolMarshaller = protocolFactory
36-
.createProtocolMarshaller(SDK_OPERATION_BINDING);
36+
.createProtocolMarshaller(SDK_OPERATION_BINDING);
3737
return protocolMarshaller.marshall(streamingInputOperationRequest);
3838
} catch (Exception e) {
3939
throw SdkClientException.builder().message("Unable to marshall request to JSON: " + e.getMessage()).cause(e).build();
4040
}
4141
}
4242
}
43-

codegen/src/test/resources/software/amazon/awssdk/codegen/poet/transform/streamingoutputoperationrequestmarshaller.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@
1919
@SdkInternalApi
2020
public class StreamingOutputOperationRequestMarshaller implements Marshaller<StreamingOutputOperationRequest> {
2121
private static final OperationInfo SDK_OPERATION_BINDING = OperationInfo.builder()
22-
.requestUri("/2016-03-11/streamingOutputOperation").httpMethod(SdkHttpMethod.POST).hasExplicitPayloadMember(false)
23-
.hasPayloadMembers(false).build();
22+
.requestUri("/2016-03-11/streamingOutputOperation").httpMethod(SdkHttpMethod.POST).hasExplicitPayloadMember(false)
23+
.hasImplicitPayloadMembers(false).hasPayloadMembers(false).build();
2424

2525
private final BaseAwsJsonProtocolFactory protocolFactory;
2626

@@ -33,11 +33,10 @@ public SdkHttpFullRequest marshall(StreamingOutputOperationRequest streamingOutp
3333
Validate.paramNotNull(streamingOutputOperationRequest, "streamingOutputOperationRequest");
3434
try {
3535
ProtocolMarshaller<SdkHttpFullRequest> protocolMarshaller = protocolFactory
36-
.createProtocolMarshaller(SDK_OPERATION_BINDING);
36+
.createProtocolMarshaller(SDK_OPERATION_BINDING);
3737
return protocolMarshaller.marshall(streamingOutputOperationRequest);
3838
} catch (Exception e) {
3939
throw SdkClientException.builder().message("Unable to marshall request to JSON: " + e.getMessage()).cause(e).build();
4040
}
4141
}
4242
}
43-

core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/DefaultJsonContentTypeResolver.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
*/
2424
@SdkProtectedApi
2525
public class DefaultJsonContentTypeResolver implements JsonContentTypeResolver {
26+
private static final String REST_JSON_CONTENT_TYPE = "application/json";
2627

2728
private final String prefix;
2829

@@ -32,7 +33,9 @@ public DefaultJsonContentTypeResolver(String prefix) {
3233

3334
@Override
3435
public String resolveContentType(AwsJsonProtocolMetadata protocolMetadata) {
35-
//Changing this to 'application/json' may break clients expecting 'application/x-amz-json-1.1'
36+
if (AwsJsonProtocol.REST_JSON.equals(protocolMetadata.protocol())) {
37+
return REST_JSON_CONTENT_TYPE;
38+
}
3639
return prefix + protocolMetadata.protocolVersion();
3740
}
3841
}

0 commit comments

Comments
 (0)