Skip to content

Commit 13f8b56

Browse files
authored
Introduced support for Binary attribute types. (#353)
* Introduced support for Binary attribute types. Added test data example Added unit-test for JSON Format Signed-off-by: Day, Jeremy(jday) <[email protected]> * documentation tweak Signed-off-by: Day, Jeremy(jday) <[email protected]> * - Addressed review comment. - Removed the withContextAttribute(string, Integer). - This should be a seperate PR, was mixed-in by accident. Signed-off-by: Day, Jeremy(jday) <[email protected]> * Address review comments Signed-off-by: Day, Jeremy(jday) <[email protected]>
1 parent a419d8b commit 13f8b56

File tree

9 files changed

+89
-5
lines changed

9 files changed

+89
-5
lines changed

api/src/main/java/io/cloudevents/CloudEventExtension.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public interface CloudEventExtension {
3939
* Get the attribute of extension named {@code key}.
4040
*
4141
* @param key the name of the extension attribute
42-
* @return the extension value in one of the valid types String/Number/Boolean
42+
* @return the extension value in one of the valid types String/Number/Boolean/URI/OffsetDateTime/byte[]
4343
* @throws IllegalArgumentException if the key is unknown to this extension
4444
*/
4545
@Nullable

api/src/main/java/io/cloudevents/rw/CloudEventContextWriter.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import javax.annotation.ParametersAreNonnullByDefault;
2323
import java.net.URI;
2424
import java.time.OffsetDateTime;
25+
import java.util.Base64;
2526

2627
/**
2728
* Interface to write the context attributes/extensions from a {@link io.cloudevents.rw.CloudEventContextReader} to a new representation.
@@ -73,7 +74,7 @@ default CloudEventContextWriter withContextAttribute(String name, OffsetDateTime
7374
}
7475

7576
/**
76-
* Set attribute with type {@link URI}.
77+
* Set attribute with type {@link Number}.
7778
* This setter should not be invoked for specversion, because the writer should
7879
* already know the specversion or because it doesn't need it to correctly write the value.
7980
*
@@ -102,4 +103,18 @@ default CloudEventContextWriter withContextAttribute(String name, Boolean value)
102103
return withContextAttribute(name, value.toString());
103104
}
104105

106+
/**
107+
* Set attribute with a binary type.
108+
* This setter should not be invoked for specversion, because the writer should
109+
* already know the specversion or because it doesn't need it to correctly write the value.
110+
*
111+
* @param name name of the attribute
112+
* @param value value of the attribute
113+
* @return self
114+
* @throws CloudEventRWException if anything goes wrong while writing this extension.
115+
* @throws IllegalArgumentException if you're trying to set the specversion attribute.
116+
*/
117+
default CloudEventContextWriter withContextAttribute(String name, byte[] value) throws CloudEventRWException {
118+
return withContextAttribute(name, Base64.getEncoder().encodeToString(value));
119+
}
105120
}

core/src/main/java/io/cloudevents/core/builder/CloudEventBuilder.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,15 @@ public interface CloudEventBuilder extends CloudEventWriter<CloudEvent> {
186186
*/
187187
CloudEventBuilder withExtension(@Nonnull String key, @Nonnull OffsetDateTime value);
188188

189+
/**
190+
* Set an extension with provided key and binary value
191+
*
192+
* @param key key of the extension attribute
193+
* @param value value of the extension attribute
194+
* @return self
195+
*/
196+
CloudEventBuilder withExtension(@Nonnull String key, @Nonnull byte[] value);
197+
189198
/**
190199
* Add to the builder all the extension key/values of the provided extension
191200
*

core/src/main/java/io/cloudevents/core/impl/BaseCloudEvent.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ protected void readExtensions(CloudEventContextWriter writer) throws CloudEventR
7777
writer.withContextAttribute(entry.getKey(), (URI) entry.getValue());
7878
} else if (entry.getValue() instanceof OffsetDateTime) {
7979
writer.withContextAttribute(entry.getKey(), (OffsetDateTime) entry.getValue());
80+
} else if (entry.getValue() instanceof byte[]) {
81+
writer.withContextAttribute(entry.getKey(), (byte[]) entry.getValue());
8082
} else {
8183
// This should never happen because we build that map only through our builders
8284
throw new IllegalStateException("Illegal value inside extensions map: " + entry);

core/src/main/java/io/cloudevents/core/impl/BaseCloudEventBuilder.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import io.cloudevents.CloudEventExtension;
2424
import io.cloudevents.core.builder.CloudEventBuilder;
2525
import io.cloudevents.core.data.BytesCloudEventData;
26+
import io.cloudevents.rw.CloudEventContextWriter;
2627
import io.cloudevents.rw.CloudEventRWException;
2728

2829
import javax.annotation.Nonnull;
@@ -140,6 +141,15 @@ public SELF withExtension(@Nonnull String key, @Nonnull OffsetDateTime value) {
140141
return self;
141142
}
142143

144+
@Override
145+
public CloudEventBuilder withExtension(@Nonnull String key, @Nonnull byte[] value) {
146+
if (!isValidExtensionName(key)) {
147+
throw CloudEventRWException.newInvalidExtensionName(key);
148+
}
149+
this.extensions.put(key, value);
150+
return self;
151+
}
152+
143153
@Override
144154
public SELF withoutExtension(@Nonnull String key) {
145155
this.extensions.remove(key);

core/src/test/java/io/cloudevents/core/impl/BaseCloudEventBuilderTest.java

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
package io.cloudevents.core.impl;
22

33
import static org.assertj.core.api.Assertions.assertThat;
4-
import static org.junit.jupiter.api.Assertions.assertThrows;
5-
import static org.junit.jupiter.api.Assertions.assertTrue;
4+
import static org.junit.jupiter.api.Assertions.*;
65

76
import org.junit.jupiter.api.Test;
87

@@ -72,4 +71,25 @@ public void testInvalidExtensionName() {
7271

7372
assertTrue(actualMessage.contains(expectedMessage));
7473
}
74+
75+
@Test
76+
public void testBinaryExtension() {
77+
78+
final String EXT_NAME = "verifyme";
79+
80+
CloudEvent given = CloudEventBuilder.v1(Data.V1_MIN)
81+
.withExtension(EXT_NAME, Data.BINARY_VALUE)
82+
.build();
83+
84+
// Sanity
85+
assertNotNull(given);
86+
87+
// Did the extension stick
88+
assertTrue(given.getExtensionNames().contains(EXT_NAME));
89+
assertNotNull(given.getExtension(EXT_NAME));
90+
91+
// Does the extension have the right value
92+
assertEquals(Data.BINARY_VALUE, given.getExtension(EXT_NAME));
93+
94+
}
7595
}

core/src/test/java/io/cloudevents/core/test/Data.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ public class Data {
4141
public static byte[] DATA_JSON_SERIALIZED = "{}".getBytes();
4242
public static byte[] DATA_XML_SERIALIZED = "<stuff></stuff>".getBytes();
4343
public static byte[] DATA_TEXT_SERIALIZED = "Hello World Lorena!".getBytes();
44+
public static byte[] BINARY_VALUE = { (byte) 0xE0, (byte) 0xFF, (byte) 0x00, (byte) 0x44, (byte) 0xAA }; // Base64: 4P8ARKo=
4445

4546
public static final CloudEvent V1_MIN = CloudEventBuilder.v1()
4647
.withId(ID)
@@ -108,6 +109,13 @@ public class Data {
108109
.withTime(TIME)
109110
.build();
110111

112+
public static final CloudEvent V1_WITH_BINARY_EXT = CloudEventBuilder.v1()
113+
.withId(ID)
114+
.withType(TYPE)
115+
.withSource(SOURCE)
116+
.withExtension("binary", BINARY_VALUE)
117+
.build();
118+
111119
public static final CloudEvent V03_MIN = CloudEventBuilder.v03(V1_MIN).build();
112120
public static final CloudEvent V03_WITH_JSON_DATA = CloudEventBuilder.v03(V1_WITH_JSON_DATA).build();
113121
public static final CloudEvent V03_WITH_JSON_DATA_WITH_EXT = CloudEventBuilder.v03(V1_WITH_JSON_DATA_WITH_EXT).build();
@@ -137,6 +145,18 @@ public static Stream<CloudEvent> v1Events() {
137145
);
138146
}
139147

148+
/**
149+
* Due to the nature of CE there are scenarios where an event might be serialized
150+
* in such a fashion that it can not be deserialized while retaining the orginal
151+
* type information, this varies from format-2-format
152+
*/
153+
154+
public static Stream<CloudEvent> v1NonRoundTripEvents() {
155+
return Stream.of(
156+
Data.V1_WITH_BINARY_EXT
157+
);
158+
}
159+
140160
public static Stream<CloudEvent> v03Events() {
141161
return Stream.of(
142162
Data.V03_MIN,

formats/json-jackson/src/test/java/io/cloudevents/jackson/JsonFormatTest.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,8 @@ public static Stream<Arguments> serializeTestArgumentsDefault() {
132132
Arguments.of(V1_WITH_JSON_DATA_WITH_FRACTIONAL_TIME, "v1/json_data_with_fractional_time.json"),
133133
Arguments.of(V1_WITH_JSON_DATA_WITH_EXT, "v1/json_data_with_ext.json"),
134134
Arguments.of(V1_WITH_XML_DATA, "v1/base64_xml_data.json"),
135-
Arguments.of(V1_WITH_TEXT_DATA, "v1/base64_text_data.json")
135+
Arguments.of(V1_WITH_TEXT_DATA, "v1/base64_text_data.json"),
136+
Arguments.of(V1_WITH_BINARY_EXT, "v1/binary_attr.json")
136137
);
137138
}
138139

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"specversion": "1.0",
3+
"id": "1",
4+
"type": "mock.test",
5+
"source": "http://localhost/source",
6+
"binary" : "4P8ARKo="
7+
}

0 commit comments

Comments
 (0)