Skip to content

Commit

Permalink
feat: JSON format to assume JSON content-type where possible (cloudev…
Browse files Browse the repository at this point in the history
…ents#604)

Signed-off-by: Matej Vašek <mvasek@redhat.com>
  • Loading branch information
matejvasek committed Feb 8, 2024
1 parent 7b9d020 commit 55fddb3
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -42,18 +42,21 @@
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 {
Expand All @@ -62,17 +65,20 @@ private static class JsonMessage implements CloudEventReader {
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
Expand All @@ -92,6 +98,9 @@ public <T extends CloudEventWriter<V>, V> V read(CloudEventWriterFactory<T, V> w

// 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);
}
Expand Down Expand Up @@ -257,7 +266,7 @@ public CloudEvent deserialize(JsonParser p, DeserializationContext ctxt) throws
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...
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ public static SimpleModule getCloudEventJacksonModule(JsonFormatOptions options)
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;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand All @@ -61,11 +64,14 @@ public boolean isForceIgnoreInvalidExtensionNameDeserialization() {
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;
Expand All @@ -87,12 +93,18 @@ public JsonFormatOptionsBuilder forceIgnoreInvalidExtensionNameDeserialization(b
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
);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
}

}

0 comments on commit 55fddb3

Please sign in to comment.