feat: JSON format to assume JSON content-type where possible (#604)
Signed-off-by: Matej Vašek <mvasek@redhat.com>
This commit is contained in:
parent
7b9d020acc
commit
55fddb35fc
|
@ -42,18 +42,21 @@ import java.io.IOException;
|
|||
class CloudEventDeserializer extends StdDeserializer<CloudEvent> {
|
||||
private final boolean forceExtensionNameLowerCaseDeserialization;
|
||||
private final boolean forceIgnoreInvalidExtensionNameDeserialization;
|
||||
private final boolean disableDataContentTypeDefaulting;
|
||||
|
||||
protected CloudEventDeserializer() {
|
||||
this(false, false);
|
||||
this(false, false, false);
|
||||
}
|
||||
|
||||
protected CloudEventDeserializer(
|
||||
boolean forceExtensionNameLowerCaseDeserialization,
|
||||
boolean forceIgnoreInvalidExtensionNameDeserialization
|
||||
boolean forceIgnoreInvalidExtensionNameDeserialization,
|
||||
boolean disableDataContentTypeDefaulting
|
||||
) {
|
||||
super(CloudEvent.class);
|
||||
this.forceExtensionNameLowerCaseDeserialization = forceExtensionNameLowerCaseDeserialization;
|
||||
this.forceIgnoreInvalidExtensionNameDeserialization = forceIgnoreInvalidExtensionNameDeserialization;
|
||||
this.disableDataContentTypeDefaulting = disableDataContentTypeDefaulting;
|
||||
}
|
||||
|
||||
private static class JsonMessage implements CloudEventReader {
|
||||
|
@ -62,17 +65,20 @@ class CloudEventDeserializer extends StdDeserializer<CloudEvent> {
|
|||
private final ObjectNode node;
|
||||
private final boolean forceExtensionNameLowerCaseDeserialization;
|
||||
private final boolean forceIgnoreInvalidExtensionNameDeserialization;
|
||||
private final boolean disableDataContentTypeDefaulting;
|
||||
|
||||
public JsonMessage(
|
||||
JsonParser p,
|
||||
ObjectNode node,
|
||||
boolean forceExtensionNameLowerCaseDeserialization,
|
||||
boolean forceIgnoreInvalidExtensionNameDeserialization
|
||||
boolean forceIgnoreInvalidExtensionNameDeserialization,
|
||||
boolean disableDataContentTypeDefaulting
|
||||
) {
|
||||
this.p = p;
|
||||
this.node = node;
|
||||
this.forceExtensionNameLowerCaseDeserialization = forceExtensionNameLowerCaseDeserialization;
|
||||
this.forceIgnoreInvalidExtensionNameDeserialization = forceIgnoreInvalidExtensionNameDeserialization;
|
||||
this.disableDataContentTypeDefaulting = disableDataContentTypeDefaulting;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -92,6 +98,9 @@ class CloudEventDeserializer extends StdDeserializer<CloudEvent> {
|
|||
|
||||
// Parse datacontenttype if any
|
||||
String contentType = getOptionalStringNode(this.node, this.p, "datacontenttype");
|
||||
if (!this.disableDataContentTypeDefaulting && contentType == null && this.node.has("data")) {
|
||||
contentType = "application/json";
|
||||
}
|
||||
if (contentType != null) {
|
||||
writer.withContextAttribute("datacontenttype", contentType);
|
||||
}
|
||||
|
@ -257,7 +266,7 @@ class CloudEventDeserializer extends StdDeserializer<CloudEvent> {
|
|||
ObjectNode node = ctxt.readValue(p, ObjectNode.class);
|
||||
|
||||
try {
|
||||
return new JsonMessage(p, node, this.forceExtensionNameLowerCaseDeserialization, this.forceIgnoreInvalidExtensionNameDeserialization)
|
||||
return new JsonMessage(p, node, this.forceExtensionNameLowerCaseDeserialization, this.forceIgnoreInvalidExtensionNameDeserialization, this.disableDataContentTypeDefaulting)
|
||||
.read(CloudEventBuilder::fromSpecVersion);
|
||||
} catch (RuntimeException e) {
|
||||
// Yeah this is bad but it's needed to support checked exceptions...
|
||||
|
|
|
@ -213,7 +213,7 @@ public final class JsonFormat implements EventFormat {
|
|||
ceModule.addSerializer(CloudEvent.class, new CloudEventSerializer(
|
||||
options.isForceDataBase64Serialization(), options.isForceStringSerialization()));
|
||||
ceModule.addDeserializer(CloudEvent.class, new CloudEventDeserializer(
|
||||
options.isForceExtensionNameLowerCaseDeserialization(), options.isForceIgnoreInvalidExtensionNameDeserialization()));
|
||||
options.isForceExtensionNameLowerCaseDeserialization(), options.isForceIgnoreInvalidExtensionNameDeserialization(), options.isDataContentTypeDefaultingDisabled()));
|
||||
return ceModule;
|
||||
}
|
||||
|
||||
|
|
|
@ -21,24 +21,27 @@ public final class JsonFormatOptions {
|
|||
private final boolean forceStringSerialization;
|
||||
private final boolean forceExtensionNameLowerCaseDeserialization;
|
||||
private final boolean forceIgnoreInvalidExtensionNameDeserialization;
|
||||
private final boolean disableDataContentTypeDefaulting;
|
||||
|
||||
/**
|
||||
* Create a new instance of this class options the serialization / deserialization.
|
||||
*/
|
||||
public JsonFormatOptions() {
|
||||
this(false, false, false, false);
|
||||
this(false, false, false, false, false);
|
||||
}
|
||||
|
||||
JsonFormatOptions(
|
||||
boolean forceDataBase64Serialization,
|
||||
boolean forceStringSerialization,
|
||||
boolean forceExtensionNameLowerCaseDeserialization,
|
||||
boolean forceIgnoreInvalidExtensionNameDeserialization
|
||||
boolean forceIgnoreInvalidExtensionNameDeserialization,
|
||||
boolean disableDataContentTypeDefaulting
|
||||
) {
|
||||
this.forceDataBase64Serialization = forceDataBase64Serialization;
|
||||
this.forceStringSerialization = forceStringSerialization;
|
||||
this.forceExtensionNameLowerCaseDeserialization = forceExtensionNameLowerCaseDeserialization;
|
||||
this.forceIgnoreInvalidExtensionNameDeserialization = forceIgnoreInvalidExtensionNameDeserialization;
|
||||
this.disableDataContentTypeDefaulting = disableDataContentTypeDefaulting;
|
||||
}
|
||||
|
||||
public static JsonFormatOptionsBuilder builder() {
|
||||
|
@ -61,11 +64,14 @@ public final class JsonFormatOptions {
|
|||
return this.forceIgnoreInvalidExtensionNameDeserialization;
|
||||
}
|
||||
|
||||
public boolean isDataContentTypeDefaultingDisabled() { return this.disableDataContentTypeDefaulting; }
|
||||
|
||||
public static class JsonFormatOptionsBuilder {
|
||||
private boolean forceDataBase64Serialization = false;
|
||||
private boolean forceStringSerialization = false;
|
||||
private boolean forceExtensionNameLowerCaseDeserialization = false;
|
||||
private boolean forceIgnoreInvalidExtensionNameDeserialization = false;
|
||||
private boolean disableDataContentTypeDefaulting = false;
|
||||
|
||||
public JsonFormatOptionsBuilder forceDataBase64Serialization(boolean forceDataBase64Serialization) {
|
||||
this.forceDataBase64Serialization = forceDataBase64Serialization;
|
||||
|
@ -87,12 +93,18 @@ public final class JsonFormatOptions {
|
|||
return this;
|
||||
}
|
||||
|
||||
public JsonFormatOptionsBuilder disableDataContentTypeDefaulting(boolean disableDataContentTypeDefaulting) {
|
||||
this.disableDataContentTypeDefaulting = disableDataContentTypeDefaulting;
|
||||
return this;
|
||||
}
|
||||
|
||||
public JsonFormatOptions build() {
|
||||
return new JsonFormatOptions(
|
||||
this.forceDataBase64Serialization,
|
||||
this.forceStringSerialization,
|
||||
this.forceExtensionNameLowerCaseDeserialization,
|
||||
this.forceIgnoreInvalidExtensionNameDeserialization
|
||||
this.forceIgnoreInvalidExtensionNameDeserialization,
|
||||
this.disableDataContentTypeDefaulting
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
package io.cloudevents.jackson;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.module.SimpleModule;
|
||||
import io.cloudevents.CloudEvent;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
|
||||
import static io.cloudevents.jackson.JsonFormat.getCloudEventJacksonModule;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class CloudEventDeserializerTest {
|
||||
|
||||
private static final String nonBinaryPayload = "{\n" +
|
||||
" \"specversion\" : \"1.0\",\n" +
|
||||
" \"type\" : \"com.example.someevent\",\n" +
|
||||
" \"source\" : \"/mycontext\",\n" +
|
||||
" \"subject\": null,\n" +
|
||||
" \"id\" : \"D234-1234-1234\",\n" +
|
||||
" \"time\" : \"2018-04-05T17:31:00Z\",\n" +
|
||||
" \"comexampleextension1\" : \"value\",\n" +
|
||||
" \"comexampleothervalue\" : 5,\n" +
|
||||
" \"data\" : \"I'm just a string\"\n" +
|
||||
"}";
|
||||
|
||||
private static final String binaryPayload = "{\n" +
|
||||
" \"specversion\" : \"1.0\",\n" +
|
||||
" \"type\" : \"com.example.someevent\",\n" +
|
||||
" \"source\" : \"/mycontext\",\n" +
|
||||
" \"id\" : \"D234-1234-1234\",\n" +
|
||||
" \"data_base64\" : \"eyAieHl6IjogMTIzIH0=\"\n" +
|
||||
"}";
|
||||
|
||||
@Test
|
||||
void impliedDataContentTypeNonBinaryData() throws IOException {
|
||||
ObjectMapper mapper = getObjectMapper(false);
|
||||
StringReader reader = new StringReader(nonBinaryPayload);
|
||||
CloudEvent ce = mapper.readValue(reader, CloudEvent.class);
|
||||
assertThat(ce.getDataContentType()).isEqualTo("application/json");
|
||||
|
||||
mapper = getObjectMapper(true);
|
||||
reader = new StringReader(nonBinaryPayload);
|
||||
ce = mapper.readValue(reader, CloudEvent.class);
|
||||
assertThat(ce.getDataContentType()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void impliedDataContentTypeBinaryData() throws IOException {
|
||||
final ObjectMapper mapper = getObjectMapper(false);
|
||||
StringReader reader = new StringReader(binaryPayload);
|
||||
CloudEvent ce = mapper.readValue(reader, CloudEvent.class);
|
||||
assertThat(ce.getDataContentType()).isNull();
|
||||
}
|
||||
|
||||
private static ObjectMapper getObjectMapper(boolean disableDataContentTypeDefaulting) {
|
||||
final ObjectMapper mapper = new ObjectMapper();
|
||||
final SimpleModule module = getCloudEventJacksonModule(
|
||||
JsonFormatOptions
|
||||
.builder()
|
||||
.disableDataContentTypeDefaulting(disableDataContentTypeDefaulting)
|
||||
.build()
|
||||
);
|
||||
mapper.registerModule(module);
|
||||
return mapper;
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue