/** * Generate an {@link Operation#UPDATE update} message with the given information. * * @param before the state of the record before the update; may be null * @param after the state of the record after the update; may not be null * @param source the information about the source where the update occurred; may be null * @param timestamp the timestamp for this message; may be null * @return the update message; never null */ public Struct update(Object before, Struct after, Struct source, Long timestamp) { Struct struct = new Struct(schema); struct.put(FieldName.OPERATION, Operation.UPDATE.code()); if (before != null) struct.put(FieldName.BEFORE, before); struct.put(FieldName.AFTER, after); if (source != null) struct.put(FieldName.SOURCE, source); if (timestamp != null) struct.put(FieldName.TIMESTAMP, timestamp); return struct; }
/** * Generate an {@link Operation#DELETE delete} message with the given information. * * @param before the state of the record before the delete; may be null * @param source the information about the source where the deletion occurred; may be null * @param timestamp the timestamp for this message; may be null * @return the delete message; never null */ public Struct delete(Object before, Struct source, Long timestamp) { Struct struct = new Struct(schema); struct.put(FieldName.OPERATION, Operation.DELETE.code()); if (before != null) struct.put(FieldName.BEFORE, before); if (source != null) struct.put(FieldName.SOURCE, source); if (timestamp != null) struct.put(FieldName.TIMESTAMP, timestamp); return struct; }
/** * Generate a {@link Operation#CREATE create} message with the given information. * * @param record the state of the record after creation; may not be null * @param source the information about the source where the creation occurred; may be null * @param timestamp the timestamp for this message; may be null * @return the create message; never null */ public Struct create(Object record, Struct source, Long timestamp) { Struct struct = new Struct(schema); struct.put(FieldName.OPERATION, Operation.CREATE.code()); struct.put(FieldName.AFTER, record); if (source != null) struct.put(FieldName.SOURCE, source); if (timestamp != null) struct.put(FieldName.TIMESTAMP, timestamp); return struct; }
/** * Generate a {@link Operation#READ read} message with the given information. * * @param record the state of the record as read; may not be null * @param source the information about the source that was read; may be null * @param timestamp the timestamp for this message; may be null * @return the read message; never null */ public Struct read(Object record, Struct source, Long timestamp) { Struct struct = new Struct(schema); struct.put(FieldName.OPERATION, Operation.READ.code()); struct.put(FieldName.AFTER, record); if (source != null) struct.put(FieldName.SOURCE, source); if (timestamp != null) struct.put(FieldName.TIMESTAMP, timestamp); return struct; }
/** * Obtain the operation for the given source record. * * @param record the source record; may not be null * @return the operation, or null if no valid operation was found in the record */ public static Operation operationFor(SourceRecord record) { Struct value = (Struct) record.value(); Field opField = value.schema().field(FieldName.OPERATION); if (opField != null) { return Operation.forCode(value.getString(opField.name())); } return null; } }
Document afterDoc = Document.parse(after); foundNames.add(afterDoc.getString("name")); Operation op = Operation.forCode(value.getString("op")); assertThat(op == Operation.READ || op == Operation.CREATE).isTrue(); }); if (record.value() != null) { Struct value = (Struct) record.value(); Operation op = Operation.forCode(value.getString("op")); assertThat(op).isEqualTo(Operation.DELETE); Document afterDoc = Document.parse(after); foundNames.add(afterDoc.getString("name")); Operation op = Operation.forCode(value.getString("op")); assertThat(op).isEqualTo(Operation.READ); });
assertThat(operationHeader.next().value().toString()).isEqualTo(Envelope.Operation.CREATE.code());
assertThat(operationHeader.next().value().toString()).isEqualTo(Envelope.Operation.UPDATE.code());
record.headers().addString(DEBEZIUM_OPERATION_HEADER_KEY, Envelope.Operation.DELETE.code());
assertThat(operationHeader.next().value().toString()).isEqualTo(Envelope.Operation.DELETE.code());
value.put(FieldName.OPERATION, operation.code()); value.put(FieldName.TIMESTAMP, timestamp); SourceRecord record = new SourceRecord(sourcePartition, offset, topicName, partition, keySchema, key, valueSchema, value);
assertThat(operationHeader.next().value().toString()).isEqualTo(Envelope.Operation.DELETE.code());
+ "}" ); assertThat(value.getString(FieldName.OPERATION)).isEqualTo(Operation.CREATE.code()); assertThat(value.getInt64(FieldName.TIMESTAMP)).isEqualTo(1002L); Struct actualSource = value.getStruct(FieldName.SOURCE);
assertThat(value.getString(FieldName.AFTER)).isNull(); assertThat(value.getString("patch")).isNull(); assertThat(value.getString(FieldName.OPERATION)).isEqualTo(Operation.DELETE.code()); assertThat(value.getInt64(FieldName.TIMESTAMP)).isEqualTo(1002L); Struct actualSource = value.getStruct(FieldName.SOURCE);
@Test @FixFor("DBZ-582") public void shouldGenerateRecordForDeleteEventWithoutTombstone() throws InterruptedException { RecordMakers recordMakers = new RecordMakers(filters, source, topicSelector, produced::add, false); BsonTimestamp ts = new BsonTimestamp(1000, 1); CollectionId collectionId = new CollectionId("rs0", "dbA", "c1"); ObjectId objId = new ObjectId(); Document obj = new Document("_id", objId); Document event = new Document().append("o", obj) .append("ns", "dbA.c1") .append("ts", ts) .append("h", new Long(12345678)) .append("op", "d"); RecordsForCollection records = recordMakers.forCollection(collectionId); records.recordEvent(event, 1002); assertThat(produced.size()).isEqualTo(1); SourceRecord record = produced.get(0); Struct key = (Struct) record.key(); Struct value = (Struct) record.value(); assertThat(key.schema()).isSameAs(record.keySchema()); assertThat(key.get("id")).isEqualTo(JSONSerializers.getStrict().serialize(objId)); assertThat(value.schema()).isSameAs(record.valueSchema()); assertThat(value.getString(FieldName.AFTER)).isNull(); assertThat(value.getString("patch")).isNull(); assertThat(value.getString(FieldName.OPERATION)).isEqualTo(Operation.DELETE.code()); assertThat(value.getInt64(FieldName.TIMESTAMP)).isEqualTo(1002L); Struct actualSource = value.getStruct(FieldName.SOURCE); Struct expectedSource = source.lastOffsetStruct("rs0", collectionId); assertThat(actualSource).isEqualTo(expectedSource); }
@Test public void shouldGenerateRecordForUpdateEvent() throws InterruptedException { BsonTimestamp ts = new BsonTimestamp(1000, 1); CollectionId collectionId = new CollectionId("rs0", "dbA", "c1"); ObjectId objId = new ObjectId(); Document obj = new Document().append("$set", new Document("name", "Sally")); Document event = new Document().append("o", obj) .append("o2", objId) .append("ns", "dbA.c1") .append("ts", ts) .append("h", Long.valueOf(12345678)) .append("op", "u"); RecordsForCollection records = recordMakers.forCollection(collectionId); records.recordEvent(event, 1002); assertThat(produced.size()).isEqualTo(1); SourceRecord record = produced.get(0); Struct key = (Struct) record.key(); Struct value = (Struct) record.value(); assertThat(key.schema()).isSameAs(record.keySchema()); assertThat(key.get("id")).isEqualTo(JSONSerializers.getStrict().serialize(objId)); assertThat(value.schema()).isSameAs(record.valueSchema()); // assertThat(value.getString(FieldName.BEFORE)).isNull(); assertThat(value.getString(FieldName.AFTER)).isNull(); assertThat(value.getString("patch")).isEqualTo(obj.toJson(WRITER_SETTINGS)); assertThat(value.getString(FieldName.OPERATION)).isEqualTo(Operation.UPDATE.code()); assertThat(value.getInt64(FieldName.TIMESTAMP)).isEqualTo(1002L); Struct actualSource = value.getStruct(FieldName.SOURCE); Struct expectedSource = source.lastOffsetStruct("rs0", collectionId); assertThat(actualSource).isEqualTo(expectedSource); }