Bug: Deserialization of event without data produces null.
If you serialize an event that does not have a data field using CloudEventSerializer using default setting, and then try to deserialize using CloudEventDeserializer you get a null value.
This is a bug surely.
Work-around:
.withData("text/plain", "".getBytes(StandardCharsets.UTF_8))
...This is a bug surely....
Not according to that class' contract [emphasis mine]:
——
deserialize
T deserialize(String topic, byte[] data)
Deserialize a record value from a byte array into a value or object.
Parameters:
topic - topic associated with the data
data - serialized bytes; may be null; implementations are recommended to handle null by returning a value or null rather than throwing an exception.
Returns:
deserialized typed data; may be null
——
The Kafka contract does not apply here. The bug is simply that no CloudEvent is returned, even though the record clearly contains a CloudEvent.
If this was not the case, I could not work-around the bug by putting some dummy data into the event.
I believe it is likely to do with the code that auto-detects whether binary or structured deserialization was used.
Example of record that fail:
ProducerRecord(topic=api-logs-local, partition=null, headers=RecordHeaders(headers = [], isReadOnly = false), key=urn:assetalias:External.asset.alias:prd, value=CloudEvent{id='7e139926-6b86-4cf3-9e77-aa11ba2e1f78', source=xxx.asset.alias, type='http_transaction.created.v1', time=2023-03-24T08:03:14.957447-07:00, extensions={endpoint=https://foo.api.xxx.com/v1/bar/{id}, method=GET, requestid=b5d1ad97-9da6-4134-81f5-eca830a72185, clientip=196.128.0.1, useragent=curl, status=200}}, timestamp=null)
…The bug is simply that no CloudEvent is returned, even though the record clearly contains a CloudEvent…
That IS interesting. For sure, @alexec.
Especially given this precondition…
…If you serialize an event that does not have a data field…
Let me ask you this…
- How closely does the following reproduce what you've described in your original report?
- Is my understanding correct that you would expect the assertions at
3,4and5in the code below to all fail?
import io.cloudevents.CloudEvent;
import io.cloudevents.core.message.Encoding;
import io.cloudevents.core.message.MessageReader;
import io.cloudevents.core.test.Data;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
public class CloudEventIssue540ReproducerTest {
private final CloudEvent eventHasNoDataField = CloudEventBuilder.v1()
.withId(…)
.withType(…)
.withSource(…)
.withoutData() // (0) An event that does not have a data field
.withSubject(…)
.build();
@Test
public void ceIssue540DeserializerShouldWork() {
String topic = "test";
CloudEventMessageDeserializer deserializer = new CloudEventMessageDeserializer();
ProducerRecord<Void, byte[]> inRecord = KafkaMessageFactory.createWriter(topic)
.writeBinary(eventHasNoDataField); // (1) "…serialize an event that does not have a data field…" — @alexec
MessageReader outMessage = deserializer
.deserialize(topic, inRecord.headers(), inRecord.value()); // (2) "…then try to deserialize…" — @alexec
assertThat(eventHasNoDataField.getData())
.isNotNull(); // (3) "…you get a null value…" — @alexec
assertThat(outMessage.toEvent())
.isNotNull(); // (4) "…no CloudEvent is returned…" — @alexec
assertThat(outMessage.toEvent())
.isEqualTo(eventHasNoDataField );
assertThat(outMessage.toEvent().getData())
.isNotNull(); // (5) "…you get a null value…" — @alexec
}
}
TIA.