From 59962ba6892121066ef24348cdc2c75371f3efe6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabio=20Jos=C3=A9?= Date: Sat, 26 Oct 2019 17:29:57 -0300 Subject: [PATCH] Support for data_base64 in json message format MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fabio José --- .../io/cloudevents/v1/CloudEventBuilder.java | 6 ++- .../io/cloudevents/v1/CloudEventImpl.java | 35 ++++++++++-- .../cloudevents/v1/CloudEventJacksonTest.java | 36 +++++++++++++ .../v1/http/HTTPStructuredMarshallerTest.java | 53 ------------------- 4 files changed, 73 insertions(+), 57 deletions(-) diff --git a/api/src/main/java/io/cloudevents/v1/CloudEventBuilder.java b/api/src/main/java/io/cloudevents/v1/CloudEventBuilder.java index 76305171..f7c04268 100644 --- a/api/src/main/java/io/cloudevents/v1/CloudEventBuilder.java +++ b/api/src/main/java/io/cloudevents/v1/CloudEventBuilder.java @@ -158,7 +158,11 @@ public class CloudEventBuilder implements datacontenttype, dataschema, subject, time); CloudEventImpl cloudEvent = - new CloudEventImpl(attributes, data, extensions); + new CloudEventImpl(attributes, data, extensions); + + if(data instanceof byte[]) { + cloudEvent.setDataBase64((byte[])data); + } Set> violations = getValidator().validate(cloudEvent); diff --git a/api/src/main/java/io/cloudevents/v1/CloudEventImpl.java b/api/src/main/java/io/cloudevents/v1/CloudEventImpl.java index 8e5d2d3a..36119a58 100644 --- a/api/src/main/java/io/cloudevents/v1/CloudEventImpl.java +++ b/api/src/main/java/io/cloudevents/v1/CloudEventImpl.java @@ -18,6 +18,7 @@ package io.cloudevents.v1; import java.net.URI; import java.time.ZonedDateTime; import java.util.Collections; +import java.util.HashMap; import java.util.Map; import java.util.Optional; import java.util.Set; @@ -25,14 +26,15 @@ import java.util.stream.Collectors; import javax.validation.constraints.NotNull; +import com.fasterxml.jackson.annotation.JsonAlias; import com.fasterxml.jackson.annotation.JsonAnyGetter; import com.fasterxml.jackson.annotation.JsonAnySetter; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonUnwrapped; -import com.fasterxml.jackson.annotation.JsonInclude.Include; import io.cloudevents.CloudEvent; import io.cloudevents.extensions.ExtensionFormat; @@ -46,12 +48,20 @@ import io.cloudevents.extensions.InMemoryFormat; @JsonInclude(value = Include.NON_ABSENT) public class CloudEventImpl implements CloudEvent { + public static final String EVENT_DATA_FIELD = "data"; + public static final String EVENT_DATA_BASE64_FILED = "data_base64"; + @JsonIgnore @NotNull private final AttributesImpl attributes; + @JsonIgnore private final T data; + //To use with json binary data + @JsonIgnore + private byte[] dataBase64; + @NotNull private final Map extensions; @@ -77,6 +87,14 @@ public class CloudEventImpl implements CloudEvent { return extensionsFormats; } + /** + * To handle the JSON base64 serialization + * @param data The byte array to encode as base64 + */ + void setDataBase64(byte[] data) { + this.dataBase64 = data; + } + @JsonUnwrapped @Override public AttributesImpl getAttributes() { @@ -91,7 +109,16 @@ public class CloudEventImpl implements CloudEvent { @JsonAnyGetter @Override public Map getExtensions() { - return Collections.unmodifiableMap(extensions); + Map result = new HashMap<>(extensions); + + if(null== dataBase64) { + if(null!= data) { + result.put(EVENT_DATA_FIELD, data); + } + } else { + result.put(EVENT_DATA_BASE64_FILED, dataBase64); + } + return Collections.unmodifiableMap(result); } /** @@ -119,7 +146,9 @@ public class CloudEventImpl implements CloudEvent { @JsonProperty("dataschema") URI dataschema, @JsonProperty("subject") String subject, @JsonProperty("time") ZonedDateTime time, - @JsonProperty("data") T data){ + @JsonProperty("data") + @JsonAlias("data_base64") + T data){ return CloudEventBuilder.builder() .withId(id) diff --git a/api/src/test/java/io/cloudevents/v1/CloudEventJacksonTest.java b/api/src/test/java/io/cloudevents/v1/CloudEventJacksonTest.java index 8da991bc..bdf30354 100644 --- a/api/src/test/java/io/cloudevents/v1/CloudEventJacksonTest.java +++ b/api/src/test/java/io/cloudevents/v1/CloudEventJacksonTest.java @@ -22,6 +22,8 @@ import static org.junit.Assert.assertTrue; import java.io.InputStream; import java.net.URI; +import java.time.ZonedDateTime; +import java.util.Base64; import org.junit.Rule; import org.junit.Test; @@ -281,4 +283,38 @@ public class CloudEventJacksonTest { assertEquals(expected.getWow(), ce.getData().get().getWow()); } + @Test + public void should_marshall_data_byte_array_as_data_base64() { + // setup + byte[] data = ("--mydata--" + + "\n" + + "customer=445" + + "\n" + + "invoice=5566" + + "\n" + + "---mydata---").getBytes(); + + String expected = + Base64.getEncoder().encodeToString(data); + + CloudEventImpl event = + CloudEventBuilder.builder() + .withId("0xbin") + .withSource(URI.create("/customers/445")) + .withType("customers.ordering") + .withDatacontenttype("text/plain") + .withDataschema(URI.create("http://schame.server.com/customer/order")) + .withSubject("orders.json") + .withTime(ZonedDateTime.now()) + .withData(data) + .build(); + + // act + String encoded = Json.encode(event); + + // assert + assertTrue(encoded.contains("\"data_base64\"")); + assertTrue(encoded.contains("\"" + expected +"\"")); + } + } diff --git a/api/src/test/java/io/cloudevents/v1/http/HTTPStructuredMarshallerTest.java b/api/src/test/java/io/cloudevents/v1/http/HTTPStructuredMarshallerTest.java index 35c1dea7..d86989ba 100644 --- a/api/src/test/java/io/cloudevents/v1/http/HTTPStructuredMarshallerTest.java +++ b/api/src/test/java/io/cloudevents/v1/http/HTTPStructuredMarshallerTest.java @@ -38,59 +38,6 @@ import io.cloudevents.v1.http.Marshallers; * */ public class HTTPStructuredMarshallerTest { - - @Test - public void should_marshal_all_as_json() { - // setup - String expected = "{\"data\":{\"wow\":\"yes!\"},\"id\":\"x10\",\"source\":\"/source\",\"specversion\":\"1.0\",\"type\":\"event-type\",\"datacontenttype\":\"application/json\",\"subject\":\"subject\"}"; - - Much ceData = new Much(); - ceData.setWow("yes!"); - - CloudEventImpl ce = - CloudEventBuilder.builder() - .withId("x10") - .withSource(URI.create("/source")) - .withType("event-type") - .withDatacontenttype("application/json") - .withSubject("subject") - .withData(ceData) - .build(); - - // act - Wire actual = - Marshallers.structured() - .withEvent(() -> ce) - .marshal(); - - assertTrue(actual.getPayload().isPresent()); - assertEquals(expected, actual.getPayload().get()); - } - - @Test - public void should_marshal_data_as_text_and_evelope_as_json() { - // setup - String expected = "{\"data\":\"yes!\",\"id\":\"x10\",\"source\":\"/source\",\"specversion\":\"1.0\",\"type\":\"event-type\",\"datacontenttype\":\"text/plain\"}"; - String ceData = "yes!"; - - CloudEventImpl ce = - CloudEventBuilder.builder() - .withId("x10") - .withSource(URI.create("/source")) - .withType("event-type") - .withDatacontenttype("text/plain") - .withData(ceData) - .build(); - - // act - Wire actual = - Marshallers.structured() - .withEvent(() -> ce) - .marshal(); - - assertTrue(actual.getPayload().isPresent()); - assertEquals(expected, actual.getPayload().get()); - } @Test public void should_headers_have_content_type() {