Skip to content

Commit 857db19

Browse files
joewyzadwsingh
andauthored
Add content-type header and empty body for empty structure payload in restJSON request (#912)
* Add content-type header and empty body for empty structure payload in restJSON * Remove the usage of stream and optional * Add usage var and modify condition checking * Move the handling to HttpBindingSerializer * Update http/http-binding/src/main/java/software/amazon/smithy/java/http/binding/HttpBindingSerializer.java Co-authored-by: Adwait Kumar Singh <[email protected]> --------- Co-authored-by: Adwait Kumar Singh <[email protected]>
1 parent 62ce87f commit 857db19

File tree

5 files changed

+56
-8
lines changed

5 files changed

+56
-8
lines changed

aws/client/aws-client-restjson/src/it/java/software/amazon/smithy/java/client/aws/restjson/RestJson1ProtocolTests.java

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,6 @@ public class RestJson1ProtocolTests {
3535
@HttpClientRequestTests
3636
@ProtocolTestFilter(
3737
skipTests = {
38-
// TODO: These tests require a payload even when the httpPayload member is null. Should it?
39-
"RestJsonHttpWithHeadersButNoPayload",
40-
"RestJsonHttpWithEmptyStructurePayload",
4138
"RestJsonHttpEmptyPrefixHeadersRequestClient" //FIXME https://github.com/smithy-lang/smithy-java/issues/647
4239
})
4340
public void requestTest(DataStream expected, DataStream actual) {

aws/client/aws-client-restjson/src/main/java/software/amazon/smithy/java/aws/client/restjson/RestJsonClientProtocol.java

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
package software.amazon.smithy.java.aws.client.restjson;
77

8+
import java.net.URI;
89
import software.amazon.smithy.aws.traits.protocols.RestJson1Trait;
910
import software.amazon.smithy.java.aws.events.AwsEventDecoderFactory;
1011
import software.amazon.smithy.java.aws.events.AwsEventEncoderFactory;
@@ -17,14 +18,20 @@
1718
import software.amazon.smithy.java.client.http.binding.HttpBindingClientProtocol;
1819
import software.amazon.smithy.java.client.http.binding.HttpBindingErrorFactory;
1920
import software.amazon.smithy.java.context.Context;
21+
import software.amazon.smithy.java.core.schema.ApiOperation;
2022
import software.amazon.smithy.java.core.schema.InputEventStreamingApiOperation;
2123
import software.amazon.smithy.java.core.schema.OutputEventStreamingApiOperation;
24+
import software.amazon.smithy.java.core.schema.SerializableStruct;
25+
import software.amazon.smithy.java.core.schema.TraitKey;
2226
import software.amazon.smithy.java.core.serde.Codec;
2327
import software.amazon.smithy.java.core.serde.event.EventDecoderFactory;
2428
import software.amazon.smithy.java.core.serde.event.EventEncoderFactory;
2529
import software.amazon.smithy.java.core.serde.event.EventStreamingException;
30+
import software.amazon.smithy.java.http.api.HttpRequest;
31+
import software.amazon.smithy.java.http.binding.RequestSerializer;
2632
import software.amazon.smithy.java.json.JsonCodec;
2733
import software.amazon.smithy.model.shapes.ShapeId;
34+
import software.amazon.smithy.model.shapes.ShapeType;
2835

2936
/**
3037
* Implements aws.protocols#restJson1.
@@ -55,6 +62,29 @@ public RestJsonClientProtocol(ShapeId service) {
5562
.build();
5663
}
5764

65+
@Override
66+
public <I extends SerializableStruct, O extends SerializableStruct> HttpRequest createRequest(
67+
ApiOperation<I, O> operation,
68+
I input,
69+
Context context,
70+
URI endpoint
71+
) {
72+
RequestSerializer serializer = httpBinding().requestSerializer()
73+
.operation(operation)
74+
.payloadCodec(payloadCodec())
75+
.payloadMediaType(payloadMediaType())
76+
.shapeValue(input)
77+
.endpoint(endpoint)
78+
.omitEmptyPayload(omitEmptyPayload())
79+
.allowEmptyStructPayload(hasStructPayload(input));
80+
81+
if (operation instanceof InputEventStreamingApiOperation<?, ?, ?> i) {
82+
serializer.eventEncoderFactory(getEventEncoderFactory(i));
83+
}
84+
85+
return serializer.serializeRequest();
86+
}
87+
5888
@Override
5989
public Codec payloadCodec() {
6090
return codec;
@@ -94,6 +124,17 @@ protected EventDecoderFactory<AwsEventFrame> getEventDecoderFactory(
94124
return AwsEventDecoderFactory.forOutputStream(outputOperation, payloadCodec(), f -> f);
95125
}
96126

127+
private <I extends SerializableStruct> boolean hasStructPayload(I input) {
128+
var members = input.schema().members();
129+
for (var member : members) {
130+
if (member.type().equals(ShapeType.STRUCTURE)
131+
&& member.hasTrait(TraitKey.HTTP_PAYLOAD_TRAIT)) {
132+
return true;
133+
}
134+
}
135+
return false;
136+
}
137+
97138
public static final class Factory implements ClientProtocolFactory<RestJson1Trait> {
98139
@Override
99140
public ShapeId id() {

http/http-binding/src/main/java/software/amazon/smithy/java/http/binding/HttpBindingSerializer.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ final class HttpBindingSerializer extends SpecificShapeSerializer implements Sha
5252
private final String payloadMediaType;
5353
private final boolean omitEmptyPayload;
5454
private final boolean isFailure;
55+
private final boolean allowEmptyStructPayload;
5556

5657
private final Map<String, String> labels = new LinkedHashMap<>();
5758
private final Map<String, List<String>> headers = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
@@ -75,7 +76,8 @@ final class HttpBindingSerializer extends SpecificShapeSerializer implements Sha
7576
String payloadMediaType,
7677
BindingMatcher bindingMatcher,
7778
boolean omitEmptyPayload,
78-
boolean isFailure
79+
boolean isFailure,
80+
boolean allowEmptyStructPayload
7981
) {
8082
uriPattern = httpTrait.getUri();
8183
responseStatus = httpTrait.getCode();
@@ -84,6 +86,7 @@ final class HttpBindingSerializer extends SpecificShapeSerializer implements Sha
8486
this.payloadMediaType = payloadMediaType;
8587
this.omitEmptyPayload = omitEmptyPayload;
8688
this.isFailure = isFailure;
89+
this.allowEmptyStructPayload = allowEmptyStructPayload;
8790
headerSerializer = new HttpHeaderSerializer(headerConsumer);
8891
querySerializer = new HttpQuerySerializer(queryStringParams::add);
8992
labelSerializer = new HttpLabelSerializer(labels::put);
@@ -95,7 +98,7 @@ public void writeStruct(Schema schema, SerializableStruct struct) {
9598
responseStatus = bindingMatcher.responseStatus();
9699
}
97100

98-
if (bindingMatcher.writeBody(omitEmptyPayload)) {
101+
if (allowEmptyStructPayload || bindingMatcher.writeBody(omitEmptyPayload)) {
99102
shapeBodyOutput = new ByteArrayOutputStream();
100103
shapeBodySerializer = payloadCodec.createSerializer(shapeBodyOutput);
101104
// Serialize only the body members to the codec.

http/http-binding/src/main/java/software/amazon/smithy/java/http/binding/RequestSerializer.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ public final class RequestSerializer {
3434
private SerializableShape shapeValue;
3535
private EventEncoderFactory<?> eventStreamEncodingFactory;
3636
private boolean omitEmptyPayload = false;
37+
private boolean allowEmptyStructPayload = false;
3738
private final ConcurrentMap<Schema, BindingMatcher> bindingCache;
3839

3940
RequestSerializer(ConcurrentMap<Schema, BindingMatcher> bindingCache) {
@@ -119,6 +120,11 @@ public RequestSerializer omitEmptyPayload(boolean omitEmptyPayload) {
119120
return this;
120121
}
121122

123+
public RequestSerializer allowEmptyStructPayload(boolean allowEmptyStructPayload) {
124+
this.allowEmptyStructPayload = allowEmptyStructPayload;
125+
return this;
126+
}
127+
122128
/**
123129
* Finishes setting up the serializer and creates an HTTP request.
124130
*
@@ -129,7 +135,6 @@ public HttpRequest serializeRequest() {
129135
Objects.requireNonNull(operation, "operation is not set");
130136
Objects.requireNonNull(payloadCodec, "payloadCodec is not set");
131137
Objects.requireNonNull(endpoint, "endpoint is not set");
132-
Objects.requireNonNull(shapeValue, "value is not set");
133138
Objects.requireNonNull(payloadMediaType, "payloadMediaType is not set");
134139

135140
var matcher = bindingCache.computeIfAbsent(operation.inputSchema(), BindingMatcher::requestMatcher);
@@ -140,7 +145,8 @@ public HttpRequest serializeRequest() {
140145
payloadMediaType,
141146
matcher,
142147
omitEmptyPayload,
143-
false);
148+
false,
149+
allowEmptyStructPayload);
144150
shapeValue.serialize(serializer);
145151
serializer.flush();
146152

http/http-binding/src/main/java/software/amazon/smithy/java/http/binding/ResponseSerializer.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,8 @@ public HttpResponse serializeResponse() {
143143
payloadMediaType,
144144
bindingCache.computeIfAbsent(schema, BindingMatcher::responseMatcher),
145145
omitEmptyPayload,
146-
isFailure);
146+
isFailure,
147+
false);
147148
shapeValue.serialize(serializer);
148149
serializer.flush();
149150

0 commit comments

Comments
 (0)