Skip to content

Commit 5719fd2

Browse files
committed
Fix rest-json marshalling for events
Correctly propagate the existence of implicit event members down to the marshaller.
1 parent b7fed13 commit 5719fd2

File tree

9 files changed

+247
-15
lines changed

9 files changed

+247
-15
lines changed

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,11 @@ public boolean hasPayloadMembers() {
233233

234234
public boolean hasImplicitPayloadMembers() {
235235
return !getUnboundMembers().isEmpty() ||
236-
(isEvent() && !getUnboundEventMembers().isEmpty());
236+
hasImplicitEventPayloadMembers();
237+
}
238+
239+
public boolean hasImplicitEventPayloadMembers() {
240+
return isEvent() && !getUnboundEventMembers().isEmpty();
237241
}
238242

239243
/**

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ protected FieldSpec operationInfoField() {
6565
.add(".hasExplicitPayloadMember($L)", shapeModel.isHasPayloadMember() ||
6666
shapeModel.getExplicitEventPayloadMember() != null)
6767
.add(".hasPayloadMembers($L)", shapeModel.hasPayloadMembers())
68+
.add(".hasImplicitPayloadMembers($L)", shapeModel.hasImplicitEventPayloadMembers())
6869
// Adding httpMethod to avoid validation failure while creating the SdkHttpFullRequest
6970
.add(".httpMethod($T.GET)", SdkHttpMethod.class)
7071
.add(".hasEvent(true)")

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

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
@SdkInternalApi
2020
public class EventOneMarshaller implements Marshaller<EventOne> {
2121
private static final OperationInfo SDK_OPERATION_BINDING = OperationInfo.builder().hasExplicitPayloadMember(false)
22-
.hasPayloadMembers(true).httpMethod(SdkHttpMethod.GET).hasEvent(true).build();
22+
.hasPayloadMembers(true).hasImplicitPayloadMembers(true).httpMethod(SdkHttpMethod.GET).hasEvent(true).build();
2323

2424
private final BaseAwsJsonProtocolFactory protocolFactory;
2525

@@ -34,11 +34,10 @@ public SdkHttpFullRequest marshall(EventOne eventOne) {
3434
ProtocolMarshaller<SdkHttpFullRequest> protocolMarshaller = protocolFactory
3535
.createProtocolMarshaller(SDK_OPERATION_BINDING);
3636
return protocolMarshaller.marshall(eventOne).toBuilder().putHeader(":message-type", "event")
37-
.putHeader(":event-type", eventOne.sdkEventType().toString()).putHeader(":content-type", protocolFactory.getContentType())
38-
.build();
37+
.putHeader(":event-type", eventOne.sdkEventType().toString())
38+
.putHeader(":content-type", protocolFactory.getContentType()).build();
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/eventthreemarshaller.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
@SdkInternalApi
2020
public class EventThreeMarshaller implements Marshaller<EventThree> {
2121
private static final OperationInfo SDK_OPERATION_BINDING = OperationInfo.builder().hasExplicitPayloadMember(false)
22-
.hasPayloadMembers(true).httpMethod(SdkHttpMethod.GET).hasEvent(true).build();
22+
.hasPayloadMembers(true).hasImplicitPayloadMembers(true).httpMethod(SdkHttpMethod.GET).hasEvent(true).build();
2323

2424
private final BaseAwsJsonProtocolFactory protocolFactory;
2525

@@ -41,4 +41,3 @@ public SdkHttpFullRequest marshall(EventThree eventThree) {
4141
}
4242
}
4343
}
44-

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

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
@SdkInternalApi
2020
public class EventTwoMarshaller implements Marshaller<EventTwo> {
2121
private static final OperationInfo SDK_OPERATION_BINDING = OperationInfo.builder().hasExplicitPayloadMember(false)
22-
.hasPayloadMembers(true).httpMethod(SdkHttpMethod.GET).hasEvent(true).build();
22+
.hasPayloadMembers(true).hasImplicitPayloadMembers(true).httpMethod(SdkHttpMethod.GET).hasEvent(true).build();
2323

2424
private final BaseAwsJsonProtocolFactory protocolFactory;
2525

@@ -34,11 +34,10 @@ public SdkHttpFullRequest marshall(EventTwo eventTwo) {
3434
ProtocolMarshaller<SdkHttpFullRequest> protocolMarshaller = protocolFactory
3535
.createProtocolMarshaller(SDK_OPERATION_BINDING);
3636
return protocolMarshaller.marshall(eventTwo).toBuilder().putHeader(":message-type", "event")
37-
.putHeader(":event-type", eventTwo.sdkEventType().toString()).putHeader(":content-type", protocolFactory.getContentType())
38-
.build();
37+
.putHeader(":event-type", eventTwo.sdkEventType().toString())
38+
.putHeader(":content-type", protocolFactory.getContentType()).build();
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/inputeventmarshaller.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
@SdkInternalApi
2020
public class InputEventMarshaller implements Marshaller<InputEvent> {
2121
private static final OperationInfo SDK_OPERATION_BINDING = OperationInfo.builder().hasExplicitPayloadMember(true)
22-
.hasPayloadMembers(true).httpMethod(SdkHttpMethod.GET).hasEvent(true).build();
22+
.hasPayloadMembers(true).hasImplicitPayloadMembers(false).httpMethod(SdkHttpMethod.GET).hasEvent(true).build();
2323

2424
private final BaseAwsJsonProtocolFactory protocolFactory;
2525

@@ -41,4 +41,3 @@ public SdkHttpFullRequest marshall(InputEvent inputEvent) {
4141
}
4242
}
4343
}
44-

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
@SdkInternalApi
2020
public class InputEventTwoMarshaller implements Marshaller<InputEventTwo> {
2121
private static final OperationInfo SDK_OPERATION_BINDING = OperationInfo.builder().hasExplicitPayloadMember(false)
22-
.hasPayloadMembers(true).httpMethod(SdkHttpMethod.GET).hasEvent(true).build();
22+
.hasPayloadMembers(true).hasImplicitPayloadMembers(true).httpMethod(SdkHttpMethod.GET).hasEvent(true).build();
2323

2424
private final BaseAwsJsonProtocolFactory protocolFactory;
2525

@@ -41,4 +41,3 @@ public SdkHttpFullRequest marshall(InputEventTwo inputEventTwo) {
4141
}
4242
}
4343
}
44-

test/protocol-tests/src/main/resources/codegen-resources/restjson/contenttype/service-2.json

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,15 @@
5151
"requestUri": "no-payload"
5252
},
5353
"input": {"shape": "NoPayloadPostRequest"}
54+
},
55+
"TestEventStream": {
56+
"name": "TestEventStream",
57+
"http": {
58+
"method": "POST",
59+
"requestUri": "eventstream"
60+
},
61+
"input": {"shape": "TestEventStreamRequest"}
62+
5463
}
5564
},
5665
"shapes":{
@@ -163,6 +172,101 @@
163172
},
164173
"documentation":"<p> The request structure for a blob payload request. </p>",
165174
"payload":"data"
175+
},
176+
"TestEventStreamRequest": {
177+
"type": "structure",
178+
"required": [
179+
"InputEventStream"
180+
],
181+
"members": {
182+
"InputEventStream": {
183+
"shape": "InputEventStream"
184+
}
185+
},
186+
"payload":"InputEventStream"
187+
},
188+
"InputEventStream": {
189+
"type": "structure",
190+
"members": {
191+
"BlobAndHeadersEvent": {
192+
"shape": "BlobAndHeadersEvent"
193+
},
194+
"HeadersOnlyEvent": {
195+
"shape": "HeadersOnlyEvent"
196+
},
197+
"ImplicitPayloadAndHeadersEvent": {
198+
"shape": "ImplicitPayloadAndHeadersEvent"
199+
}
200+
},
201+
"eventstream": true
202+
},
203+
"BlobAndHeadersEvent": {
204+
"type": "structure",
205+
"members": {
206+
"BlobPayloadMember": {
207+
"shape":"BlobPayloadMember",
208+
"eventpayload":true
209+
},
210+
"HeaderMember": {
211+
"shape": "String",
212+
"eventheader": true
213+
}
214+
},
215+
"event": true
216+
},
217+
"ImplicitPayloadAndHeadersEvent": {
218+
"type": "structure",
219+
"members": {
220+
"StringMember": {
221+
"shape":"String"
222+
},
223+
"HeaderMember": {
224+
"shape": "String",
225+
"eventheader": true
226+
}
227+
},
228+
"event": true
229+
},
230+
"HeadersOnlyEvent": {
231+
"type": "structure",
232+
"members": {
233+
"HeaderMember": {
234+
"shape": "String",
235+
"eventheader": true
236+
}
237+
},
238+
"event": true
239+
},
240+
"BlobPayloadMember":{"type":"blob"},
241+
"EventStream": {
242+
"type": "structure",
243+
"members": {
244+
"EventOne": {
245+
"shape": "EventOne"
246+
},
247+
"EventTwo": {
248+
"shape": "EventTwo"
249+
}
250+
},
251+
"eventstream": true
252+
},
253+
"EventOne": {
254+
"type": "structure",
255+
"members": {
256+
"Foo": {
257+
"shape": "String"
258+
}
259+
},
260+
"event": true
261+
},
262+
"EventTwo": {
263+
"type": "structure",
264+
"members": {
265+
"Bar": {
266+
"shape": "String"
267+
}
268+
},
269+
"event": true
166270
}
167271
}
168272
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package software.amazon.awssdk.protocol.tests;
17+
18+
import static org.assertj.core.api.Assertions.assertThat;
19+
20+
import java.net.URI;
21+
import java.nio.charset.StandardCharsets;
22+
import org.junit.Test;
23+
import software.amazon.awssdk.core.SdkBytes;
24+
import software.amazon.awssdk.core.client.config.SdkClientConfiguration;
25+
import software.amazon.awssdk.core.client.config.SdkClientOption;
26+
import software.amazon.awssdk.http.ContentStreamProvider;
27+
import software.amazon.awssdk.http.SdkHttpFullRequest;
28+
import software.amazon.awssdk.protocols.json.AwsJsonProtocol;
29+
import software.amazon.awssdk.protocols.json.AwsJsonProtocolFactory;
30+
import software.amazon.awssdk.services.protocolrestjsoncontenttype.model.BlobAndHeadersEvent;
31+
import software.amazon.awssdk.services.protocolrestjsoncontenttype.model.HeadersOnlyEvent;
32+
import software.amazon.awssdk.services.protocolrestjsoncontenttype.model.ImplicitPayloadAndHeadersEvent;
33+
import software.amazon.awssdk.services.protocolrestjsoncontenttype.model.InputEventStream;
34+
35+
import software.amazon.awssdk.services.protocolrestjsoncontenttype.model.ProtocolRestJsonContentTypeException;
36+
import software.amazon.awssdk.services.protocolrestjsoncontenttype.transform.BlobAndHeadersEventMarshaller;
37+
import software.amazon.awssdk.services.protocolrestjsoncontenttype.transform.HeadersOnlyEventMarshaller;
38+
import software.amazon.awssdk.services.protocolrestjsoncontenttype.transform.ImplicitPayloadAndHeadersEventMarshaller;
39+
40+
public class RestJsonEventStreamProtocolTest {
41+
private static final String EVENT_CONTENT_TYPE_HEADER = ":content-type";
42+
43+
@Test
44+
public void implicitPayloadAndHeaders_payloadMemberPresent() {
45+
ImplicitPayloadAndHeadersEventMarshaller marshaller = new ImplicitPayloadAndHeadersEventMarshaller(protocolFactory());
46+
47+
ImplicitPayloadAndHeadersEvent event = InputEventStream.implicitPayloadAndHeadersEventBuilder()
48+
.stringMember("hello rest-json")
49+
.headerMember("hello rest-json")
50+
.build();
51+
52+
SdkHttpFullRequest marshalledEvent = marshaller.marshall(event);
53+
54+
assertThat(marshalledEvent.headers().get(EVENT_CONTENT_TYPE_HEADER)).containsExactly("application/json");
55+
56+
String content = contentAsString(marshalledEvent);
57+
assertThat(content).isEqualTo("{\"StringMember\":\"hello rest-json\"}");
58+
}
59+
60+
@Test
61+
public void implicitPayloadAndHeaders_payloadMemberNotPresent() {
62+
ImplicitPayloadAndHeadersEventMarshaller marshaller = new ImplicitPayloadAndHeadersEventMarshaller(protocolFactory());
63+
64+
ImplicitPayloadAndHeadersEvent event = InputEventStream.implicitPayloadAndHeadersEventBuilder()
65+
.headerMember("hello rest-json")
66+
.build();
67+
68+
SdkHttpFullRequest marshalledEvent = marshaller.marshall(event);
69+
70+
assertThat(marshalledEvent.headers().get(EVENT_CONTENT_TYPE_HEADER)).containsExactly("application/json");
71+
72+
String content = contentAsString(marshalledEvent);
73+
assertThat(content).isEqualTo("{}");
74+
}
75+
76+
@Test
77+
public void blobAndHeadersEvent() {
78+
BlobAndHeadersEventMarshaller marshaller = new BlobAndHeadersEventMarshaller(protocolFactory());
79+
80+
BlobAndHeadersEvent event = InputEventStream.blobAndHeadersEventBuilder()
81+
.headerMember("hello rest-json")
82+
.blobPayloadMember(SdkBytes.fromUtf8String("hello rest-json"))
83+
.build();
84+
85+
SdkHttpFullRequest marshalledEvent = marshaller.marshall(event);
86+
87+
assertThat(marshalledEvent.headers().get(EVENT_CONTENT_TYPE_HEADER)).containsExactly("application/octet-stream");
88+
89+
String content = contentAsString(marshalledEvent);
90+
assertThat(content).isEqualTo("hello rest-json");
91+
}
92+
93+
@Test
94+
public void headersOnly() {
95+
HeadersOnlyEventMarshaller marshaller = new HeadersOnlyEventMarshaller(protocolFactory());
96+
97+
HeadersOnlyEvent event = InputEventStream.headersOnlyEventBuilder()
98+
.headerMember("hello rest-json")
99+
.build();
100+
101+
SdkHttpFullRequest marshalledEvent = marshaller.marshall(event);
102+
103+
assertThat(marshalledEvent.headers().keySet()).doesNotContain(EVENT_CONTENT_TYPE_HEADER);
104+
105+
String content = contentAsString(marshalledEvent);
106+
assertThat(content).isEqualTo("");
107+
}
108+
109+
private static AwsJsonProtocolFactory protocolFactory() {
110+
return AwsJsonProtocolFactory.builder()
111+
.clientConfiguration(
112+
SdkClientConfiguration.builder()
113+
.option(SdkClientOption.ENDPOINT,
114+
URI.create("https://test.aws.com"))
115+
.build())
116+
.defaultServiceExceptionSupplier(ProtocolRestJsonContentTypeException::builder)
117+
.protocol(AwsJsonProtocol.REST_JSON)
118+
.protocolVersion("1.1")
119+
.build();
120+
}
121+
122+
private static String contentAsString(SdkHttpFullRequest request) {
123+
return request.contentStreamProvider()
124+
.map(ContentStreamProvider::newStream)
125+
.map(s -> SdkBytes.fromInputStream(s).asString(StandardCharsets.UTF_8))
126+
.orElse(null);
127+
}
128+
}

0 commit comments

Comments
 (0)