
io.debezium.bindings.kafka.KafkaDebeziumSinkRecord Maven / Gradle / Ivy
/*
* Copyright Debezium Authors.
*
* Licensed under the Apache Software License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package io.debezium.bindings.kafka;
import java.util.List;
import org.apache.kafka.connect.data.Schema;
import org.apache.kafka.connect.data.SchemaBuilder;
import org.apache.kafka.connect.data.Struct;
import org.apache.kafka.connect.errors.ConnectException;
import org.apache.kafka.connect.header.Header;
import org.apache.kafka.connect.sink.SinkRecord;
import io.debezium.annotation.Immutable;
import io.debezium.data.Envelope;
import io.debezium.sink.DebeziumSinkRecord;
import io.debezium.sink.SinkConnectorConfig;
import io.debezium.util.Strings;
@Immutable
public class KafkaDebeziumSinkRecord implements DebeziumSinkRecord {
protected final SinkRecord originalKafkaRecord;
public KafkaDebeziumSinkRecord(SinkRecord record) {
this.originalKafkaRecord = record;
}
@Override
public String topicName() {
return originalKafkaRecord.topic();
}
@Override
public Integer partition() {
return originalKafkaRecord.kafkaPartition();
}
@Override
public long offset() {
return originalKafkaRecord.kafkaOffset();
}
@Override
public List keyFieldNames() {
throw new RuntimeException("Not implemented");
}
@Override
public Object key() {
return originalKafkaRecord.key();
}
@Override
public Schema keySchema() {
return originalKafkaRecord.keySchema();
}
public Object value() {
return originalKafkaRecord.value();
}
@Override
public Schema valueSchema() {
return originalKafkaRecord.valueSchema();
}
@Override
public boolean isDebeziumMessage() {
return originalKafkaRecord.value() != null && originalKafkaRecord.valueSchema().name() != null && originalKafkaRecord.valueSchema().name().contains("Envelope");
}
public boolean isSchemaChange() {
return originalKafkaRecord.valueSchema() != null
&& !Strings.isNullOrEmpty(originalKafkaRecord.valueSchema().name())
&& originalKafkaRecord.valueSchema().name().contains(SCHEMA_CHANGE_VALUE);
}
public boolean isFlattened() {
return !isTombstone() && (originalKafkaRecord.valueSchema().name() == null || !originalKafkaRecord.valueSchema().name().contains("Envelope"));
}
@Override
public boolean isTombstone() {
// NOTE
// Debezium TOMBSTONE has both value and valueSchema to null, instead of the ExtractNewRecordState SMT with delete.handling.mode=none
// which will generate a record with value null that should be treated as a flattened delete. See isDelete method.
return originalKafkaRecord.value() == null && originalKafkaRecord.valueSchema() == null;
}
@Override
public boolean isDelete() {
if (!isDebeziumMessage()) {
return originalKafkaRecord.value() == null;
}
else if (originalKafkaRecord.value() != null) {
final Struct value = (Struct) originalKafkaRecord.value();
return Envelope.Operation.DELETE.equals(Envelope.Operation.forCode(value.getString(Envelope.FieldName.OPERATION)));
}
return false;
}
@Override
public boolean isTruncate() {
if (isDebeziumMessage()) {
final Struct value = (Struct) originalKafkaRecord.value();
return Envelope.Operation.TRUNCATE.equals(Envelope.Operation.forCode(value.getString(Envelope.FieldName.OPERATION)));
}
return false;
}
public Struct getPayload() {
if (isDebeziumMessage()) {
return ((Struct) originalKafkaRecord.value()).getStruct(Envelope.FieldName.AFTER);
}
else {
return ((Struct) originalKafkaRecord.value());
}
}
@Override
public Struct getKeyStruct(SinkConnectorConfig.PrimaryKeyMode primaryKeyMode) {
if (!keyFieldNames().isEmpty()) {
switch (primaryKeyMode) {
case RECORD_KEY:
final Schema keySchema = originalKafkaRecord.keySchema();
if (keySchema != null && Schema.Type.STRUCT.equals(keySchema.type())) {
return (Struct) originalKafkaRecord.key();
}
else {
throw new ConnectException("No struct-based primary key defined for record key.");
}
case RECORD_VALUE:
final Schema valueSchema = originalKafkaRecord.valueSchema();
if (valueSchema != null && Schema.Type.STRUCT.equals(valueSchema.type())) {
return getPayload();
}
else {
throw new ConnectException("No struct-based primary key defined for record value.");
}
case RECORD_HEADER:
final SchemaBuilder headerSchemaBuilder = SchemaBuilder.struct();
originalKafkaRecord.headers().forEach((Header header) -> headerSchemaBuilder.field(header.key(), header.schema()));
final Schema headerSchema = headerSchemaBuilder.build();
final Struct headerStruct = new Struct(headerSchema);
originalKafkaRecord.headers().forEach((Header header) -> headerStruct.put(header.key(), header.value()));
return headerStruct;
}
}
return null;
}
public SinkRecord getOriginalKafkaRecord() {
return originalKafkaRecord;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy