Skip to content

Commit efb7a93

Browse files
authored
Properly handle event payload members of blob and string type (#1064)
1 parent 3ad21b6 commit efb7a93

15 files changed

Lines changed: 815 additions & 210 deletions

File tree

aws/aws-event-streams/src/main/java/software/amazon/smithy/java/aws/events/AwsEventShapeDecoder.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
* @param <E> The type of the event
3131
* @param <IR> The type of the initial event
3232
*/
33-
public final class AwsEventShapeDecoder<E extends SerializableStruct, IR extends SerializableStruct>
33+
final class AwsEventShapeDecoder<E extends SerializableStruct, IR extends SerializableStruct>
3434
implements EventDecoder<AwsEventFrame> {
3535

3636
private final InitialEventType initialEventType;

aws/aws-event-streams/src/main/java/software/amazon/smithy/java/aws/events/AwsEventShapeEncoder.java

Lines changed: 266 additions & 130 deletions
Large diffs are not rendered by default.

aws/aws-event-streams/src/main/java/software/amazon/smithy/java/aws/events/EventHeaderSerializer.java

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,57 +7,55 @@
77

88
import java.nio.ByteBuffer;
99
import java.time.Instant;
10-
import java.util.Map;
11-
import software.amazon.eventstream.HeaderValue;
1210
import software.amazon.smithy.java.core.schema.Schema;
1311
import software.amazon.smithy.java.core.schema.SerializableStruct;
1412
import software.amazon.smithy.java.core.serde.SpecificShapeSerializer;
1513

16-
public class EventHeaderSerializer extends SpecificShapeSerializer {
17-
private final Map<String, HeaderValue> headers;
14+
class EventHeaderSerializer extends SpecificShapeSerializer {
15+
private final HeadersBuilder headers;
1816

19-
public EventHeaderSerializer(Map<String, HeaderValue> headers) {
17+
public EventHeaderSerializer(HeadersBuilder headers) {
2018
this.headers = headers;
2119
}
2220

2321
@Override
2422
public void writeBoolean(Schema schema, boolean value) {
25-
headers.put(schema.memberName(), HeaderValue.fromBoolean(value));
23+
headers.put(schema.memberName(), value);
2624
}
2725

2826
@Override
2927
public void writeShort(Schema schema, short value) {
30-
headers.put(schema.memberName(), HeaderValue.fromShort(value));
28+
headers.put(schema.memberName(), value);
3129
}
3230

3331
@Override
3432
public void writeByte(Schema schema, byte value) {
35-
headers.put(schema.memberName(), HeaderValue.fromByte(value));
33+
headers.put(schema.memberName(), value);
3634
}
3735

3836
@Override
3937
public void writeInteger(Schema schema, int value) {
40-
headers.put(schema.memberName(), HeaderValue.fromInteger(value));
38+
headers.put(schema.memberName(), value);
4139
}
4240

4341
@Override
4442
public void writeLong(Schema schema, long value) {
45-
headers.put(schema.memberName(), HeaderValue.fromLong(value));
43+
headers.put(schema.memberName(), value);
4644
}
4745

4846
@Override
4947
public void writeString(Schema schema, String value) {
50-
headers.put(schema.memberName(), HeaderValue.fromString(value));
48+
headers.put(schema.memberName(), value);
5149
}
5250

5351
@Override
5452
public void writeBlob(Schema schema, ByteBuffer value) {
55-
headers.put(schema.memberName(), HeaderValue.fromByteBuffer(value));
53+
headers.put(schema.memberName(), value);
5654
}
5755

5856
@Override
5957
public void writeTimestamp(Schema schema, Instant value) {
60-
headers.put(schema.memberName(), HeaderValue.fromTimestamp(value));
58+
headers.put(schema.memberName(), value);
6159
}
6260

6361
@Override
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package software.amazon.smithy.java.aws.events;
7+
8+
import java.nio.ByteBuffer;
9+
import java.time.Instant;
10+
import java.util.HashMap;
11+
import java.util.Map;
12+
import software.amazon.eventstream.HeaderValue;
13+
14+
/**
15+
* Util class to build headers for AWS events.
16+
*/
17+
final class HeadersBuilder {
18+
19+
private final Map<String, HeaderValue> headers = new HashMap<>();
20+
21+
HeadersBuilder() {}
22+
23+
/**
24+
* Returns a headers builder for messages of type event.
25+
*/
26+
static HeadersBuilder forEvent() {
27+
return new HeadersBuilder()
28+
.messageType(MessageType.EVENT);
29+
}
30+
31+
/**
32+
* Returns a headers builder for messages of type error.
33+
*/
34+
static HeadersBuilder forError() {
35+
return new HeadersBuilder()
36+
.messageType(MessageType.ERROR);
37+
}
38+
39+
/**
40+
* Returns a headers builder for messages of type exception.
41+
*/
42+
static HeadersBuilder forException() {
43+
return new HeadersBuilder()
44+
.messageType(MessageType.EXCEPTION);
45+
}
46+
47+
public HeadersBuilder messageType(MessageType messageType) {
48+
headers.put(":message-type", HeaderValue.fromString(messageType.toString()));
49+
return this;
50+
}
51+
52+
/**
53+
* Sets the `:content-type` header.
54+
*/
55+
public HeadersBuilder contentType(String contentType) {
56+
headers.put(":content-type", HeaderValue.fromString(contentType));
57+
return this;
58+
}
59+
60+
/**
61+
* Sets the `:event-type` header.
62+
*/
63+
public HeadersBuilder eventType(String eventType) {
64+
headers.put(":event-type", HeaderValue.fromString(eventType));
65+
return this;
66+
}
67+
68+
/**
69+
* Sets the `:exception-type` header.
70+
*/
71+
public HeadersBuilder exceptionType(String eventType) {
72+
headers.put(":exception-type", HeaderValue.fromString(eventType));
73+
return this;
74+
}
75+
76+
/**
77+
* Sets the `:error-code` header.
78+
*/
79+
public HeadersBuilder errorCode(String errorCode) {
80+
headers.put(":error-code", HeaderValue.fromString(errorCode));
81+
return this;
82+
}
83+
84+
/**
85+
* Sets the `:error-message` header.
86+
*/
87+
public HeadersBuilder errorMessage(String errorMessage) {
88+
headers.put(":error-message", HeaderValue.fromString(errorMessage));
89+
return this;
90+
}
91+
92+
/**
93+
* Adds the header with the given name and value.
94+
*/
95+
public HeadersBuilder put(String name, String value) {
96+
headers.put(name, HeaderValue.fromString(value));
97+
return this;
98+
}
99+
100+
/**
101+
* Adds the header with the given name and value.
102+
*/
103+
public HeadersBuilder put(String name, int value) {
104+
headers.put(name, HeaderValue.fromInteger(value));
105+
return this;
106+
}
107+
108+
/**
109+
* Adds the header with the given name and value.
110+
*/
111+
public HeadersBuilder put(String name, long value) {
112+
headers.put(name, HeaderValue.fromLong(value));
113+
return this;
114+
}
115+
116+
/**
117+
* Adds the header with the given name and value.
118+
*/
119+
public HeadersBuilder put(String name, boolean value) {
120+
headers.put(name, HeaderValue.fromBoolean(value));
121+
return this;
122+
}
123+
124+
/**
125+
* Adds the header with the given name and value.
126+
*/
127+
public HeadersBuilder put(String name, short value) {
128+
headers.put(name, HeaderValue.fromShort(value));
129+
return this;
130+
}
131+
132+
/**
133+
* Adds the header with the given name and value.
134+
*/
135+
public HeadersBuilder put(String name, byte value) {
136+
headers.put(name, HeaderValue.fromByte(value));
137+
return this;
138+
}
139+
140+
/**
141+
* Adds the header with the given name and value.
142+
*/
143+
public HeadersBuilder put(String name, ByteBuffer value) {
144+
headers.put(name, HeaderValue.fromByteBuffer(value));
145+
return this;
146+
}
147+
148+
/**
149+
* Adds the header with the given name and value.
150+
*/
151+
public HeadersBuilder put(String name, Instant value) {
152+
headers.put(name, HeaderValue.fromTimestamp(value));
153+
return this;
154+
}
155+
156+
/**
157+
* Returns the map with the header values.
158+
*/
159+
public Map<String, HeaderValue> build() {
160+
return headers;
161+
}
162+
163+
enum MessageType {
164+
EVENT("event"),
165+
ERROR("error"),
166+
EXCEPTION("exception");
167+
168+
private final String value;
169+
170+
MessageType(String value) {
171+
this.value = value;
172+
}
173+
174+
@Override
175+
public String toString() {
176+
return value;
177+
}
178+
}
179+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package software.amazon.smithy.java.aws.events;
7+
8+
/**
9+
* A mutable container for a single value, intended to be used as an out-parameter
10+
* that can be passed through multiple layers so that any layer can set or read the value.
11+
*
12+
* <p>This class is not thread-safe.
13+
*
14+
* @param <T> the type of the held value
15+
*/
16+
final class Holder<T> {
17+
private T value;
18+
19+
/**
20+
* Creates an empty holder with no initial value.
21+
*/
22+
Holder() {}
23+
24+
/**
25+
* Creates a holder with the given initial value.
26+
*
27+
* @param value the initial value
28+
*/
29+
Holder(T value) {
30+
this.value = value;
31+
}
32+
33+
/**
34+
* Sets the held value, replacing any previously set value.
35+
*
36+
* @param value the value to set
37+
*/
38+
public void set(T value) {
39+
this.value = value;
40+
}
41+
42+
/**
43+
* Sets the held value only if no value has been set yet (i.e., the current value is {@code null}).
44+
*
45+
* @param value the value to set
46+
* @return {@code true} if the value was set, {@code false} if a value was already present
47+
*/
48+
public boolean setIfAbsent(T value) {
49+
if (this.value == null) {
50+
this.value = value;
51+
return true;
52+
}
53+
return false;
54+
}
55+
56+
/**
57+
* Returns the currently held value, or {@code null} if no value has been set.
58+
*
59+
* @return the held value, or {@code null}
60+
*/
61+
public T get() {
62+
return value;
63+
}
64+
}

aws/aws-event-streams/src/test/java/software/amazon/smithy/java/aws/events/AwsEventShapeDecoderTest.java

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ class AwsEventShapeDecoderTest {
3434
@Test
3535
public void testDecodeInitialResponse() {
3636
// Arrange
37-
var headers = new AwsEventShapeEncoderTest.HeadersBuilder()
37+
var headers = HeadersBuilder.forEvent()
3838
.eventType("initial-response")
3939
.contentType("text/json")
4040
.put("intMemberHeader", 123)
@@ -57,7 +57,7 @@ public void testDecodeInitialResponse() {
5757
@Test
5858
public void testDecodeHeadersOnlyMember() {
5959
// Arrange
60-
var headers = new AwsEventShapeEncoderTest.HeadersBuilder()
60+
var headers = HeadersBuilder.forEvent()
6161
.contentType("text/json")
6262
.eventType("headersOnlyMember")
6363
.put("sequenceNum", 123)
@@ -81,7 +81,7 @@ public void testDecodeHeadersOnlyMember() {
8181
@Test
8282
public void testDecodeStructureMember() {
8383
// Arrange
84-
var headers = new AwsEventShapeEncoderTest.HeadersBuilder()
84+
var headers = HeadersBuilder.forEvent()
8585
.contentType("text/json")
8686
.eventType("structureMember")
8787
.build();
@@ -104,7 +104,7 @@ public void testDecodeStructureMember() {
104104
@Test
105105
public void testDecodeBodyAndHeaderMember() {
106106
// Arrange
107-
var headers = new AwsEventShapeEncoderTest.HeadersBuilder()
107+
var headers = HeadersBuilder.forEvent()
108108
.contentType("text/json")
109109
.eventType("bodyAndHeaderMember")
110110
.put("intMember", 123)
@@ -131,7 +131,7 @@ public void testDecodeBodyAndHeaderMember() {
131131
@Test
132132
public void testDecodeStringMember() {
133133
// Arrange
134-
var headers = new AwsEventShapeEncoderTest.HeadersBuilder()
134+
var headers = HeadersBuilder.forEvent()
135135
.contentType("text/json")
136136
.eventType("stringMember")
137137
.build();
@@ -154,9 +154,8 @@ public void testDecodeStringMember() {
154154
@Test
155155
public void testDecodeExceptionMember() {
156156
// Arrange
157-
var headers = new AwsEventShapeEncoderTest.HeadersBuilder()
157+
var headers = HeadersBuilder.forException()
158158
.contentType("text/json")
159-
.messageType("exception")
160159
.exceptionType("modeledErrorMember")
161160
.build();
162161
var message = new Message(headers, "{\"message\":\"Client exception\"}".getBytes(StandardCharsets.UTF_8));
@@ -179,8 +178,7 @@ public void testDecodeExceptionMember() {
179178
@Test
180179
public void testDecodeError() {
181180
// Arrange
182-
var headers = new AwsEventShapeEncoderTest.HeadersBuilder()
183-
.messageType("error")
181+
var headers = HeadersBuilder.forError()
184182
.put(":error-code", "InternalFailure")
185183
.put(":error-message", "An internal server error occurred")
186184
.build();

0 commit comments

Comments
 (0)