Skip to content

Commit 36747e2

Browse files
committed
fix(mongock-importer): skip IGNORED audit entries safely
`MongockImporterMongoDB.toAuditEntry` returns `null` for entries whose state is `IGNORED` (via `MongockAuditEntry.shouldBeIgnored()`), but the returned list is iterated downstream without a null check. Booting an application against a Mongock audit history containing an `IGNORED` entry therefore crashed with: NullPointerException: Cannot invoke "AuditEntry.getSystemChange()" because "auditEntryFromOrigin" is null inside the `migration-mongock-to-flamingock-community` system change. Changes: - **MongockImporterMongoDB**: filter `null`s out of the stream produced by `getAuditHistory()` and emit an INFO log when an entry is dropped because of `IGNORED`. The skipped entry's `changeId` is reported so operators know which legacy change was bypassed. - **MongockImportChange**: defensive null-filter at the caller so the same protection applies to any current or future `AuditHistoryReader` implementation (Couchbase, DynamoDB). - **MongoDBImporterTest**: regression test seeding the basic scenario plus one `IGNORED` entry. Asserts the `IGNORED` entry is absent from the Flamingock audit store and the rest of the audit history imports normally. No public API change. Behavior is purely additive (null-safety + log) relative to the previous code path.
1 parent 8548e78 commit 36747e2

3 files changed

Lines changed: 73 additions & 1 deletion

File tree

legacy/mongock-importer-mongodb/src/main/java/io/flamingock/importer/mongock/mongodb/MongockImporterMongoDB.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,22 @@
2121
import io.flamingock.internal.common.core.audit.AuditEntry;
2222
import io.flamingock.internal.common.core.audit.AuditHistoryReader;
2323
import org.bson.Document;
24+
import org.slf4j.Logger;
25+
import org.slf4j.LoggerFactory;
2426

2527
import java.time.Instant;
2628
import java.time.LocalDateTime;
2729
import java.time.ZoneId;
2830
import java.util.ArrayList;
2931
import java.util.Date;
3032
import java.util.List;
33+
import java.util.Objects;
3134
import java.util.stream.Collectors;
3235

3336
public class MongockImporterMongoDB implements AuditHistoryReader {
3437

38+
private static final Logger logger = LoggerFactory.getLogger("MongockImporter");
39+
3540
private final MongoCollection<Document> sourceCollection;
3641

3742
public MongockImporterMongoDB(MongoDatabase mongoDatabase, String collectionName) {
@@ -44,6 +49,7 @@ public List<AuditEntry> getAuditHistory() {
4449
.into(new ArrayList<>())
4550
.stream()
4651
.map(MongockImporterMongoDB::toAuditEntry)
52+
.filter(Objects::nonNull)
4753
.collect(Collectors.toList());
4854
}
4955

@@ -55,6 +61,8 @@ private static AuditEntry toAuditEntry(Document document) {
5561
.toLocalDateTime();
5662

5763
if (changeEntry.shouldBeIgnored()) {
64+
logger.info("Skipping Mongock audit entry with changeId[{}]: state=IGNORED (Mongock never executed this change; nothing to import).",
65+
changeEntry.getChangeId());
5866
return null;
5967
}
6068
return new AuditEntry(

legacy/mongock-importer-mongodb/src/test/java/io/flamingock/importer/mongock/mongodb/MongoDBImporterTest.java

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -498,6 +498,65 @@ void GIVEN_unknownAuditEntriesAndRelaxedMode_WHEN_migratingToFlamingockCommunity
498498
);
499499
}
500500

501+
@Test
502+
@DisplayName("GIVEN Mongock audit history contains an IGNORED entry " +
503+
"WHEN migrating to Flamingock Community " +
504+
"THEN should skip the IGNORED entry without crashing " +
505+
"AND import the rest of the history normally")
506+
void GIVEN_ignoredAuditEntry_WHEN_migratingToFlamingockCommunity_THEN_shouldSkipIgnoredAndImportRest() throws java.text.ParseException {
507+
// Regression test for IGNORED-state null leak in MongockImporterMongoDB.toAuditEntry().
508+
// Before the fix, the importer returned null for IGNORED entries and the caller
509+
// (MongockImportChange.importHistory) crashed with:
510+
// NullPointerException: Cannot invoke "AuditEntry.getSystemChange()"
511+
// because "auditEntryFromOrigin" is null
512+
// The fix filters nulls at both layers and logs the skipped entry.
513+
514+
mongockTestHelper.setupBasicScenario();
515+
mongockTestHelper.write(new MongockChangeEntry(
516+
"ignored-execution-1",
517+
"ignored-change",
518+
"mongock",
519+
MongockTestHelper.DEFAULT_DATE_FORMAT.parse("2025-06-19T05:43:57.200Z"),
520+
MongockChangeState.IGNORED,
521+
io.flamingock.common.test.mongock.MongockChangeType.EXECUTION,
522+
"io.example.IgnoredChangeUnit",
523+
"apply",
524+
null,
525+
0L,
526+
MongockTestHelper.DEFAULT_HOSTNAME,
527+
null,
528+
false,
529+
null
530+
));
531+
532+
MongoDBSyncTargetSystem mongodbTargetSystem = new MongoDBSyncTargetSystem("mongodb-target-system", mongoClient, DATABASE_NAME);
533+
534+
Runner flamingock = testKit.createBuilder()
535+
.addTargetSystem(mongodbTargetSystem)
536+
.build();
537+
538+
flamingock.run();
539+
540+
// IGNORED entry must NOT appear in the Flamingock audit store.
541+
assertNull(getAuditEntryByChangeId("ignored-change"),
542+
"IGNORED Mongock entry must not be imported into the Flamingock audit store");
543+
544+
// Remaining basic-scenario entries imported as normal, plus native changes executed.
545+
auditHelper.verifyAuditSequenceStrict(
546+
APPLIED("system-change-00001_before"),
547+
APPLIED("system-change-00001"),
548+
APPLIED("mongock-change-1_before"),
549+
APPLIED("mongock-change-1"),
550+
APPLIED("mongock-change-2"),
551+
STARTED("migration-mongock-to-flamingock-community"),
552+
APPLIED("migration-mongock-to-flamingock-community"),
553+
STARTED("create-users-collection-with-index"),
554+
APPLIED("create-users-collection-with-index"),
555+
STARTED("seed-users"),
556+
APPLIED("seed-users")
557+
);
558+
}
559+
501560
@Test
502561
@DisplayName("GIVEN relaxed import flag with invalid value " +
503562
"WHEN migrating to Flamingock Community " +

legacy/mongock-support/src/main/java/io/flamingock/support/mongock/MongockImportChange.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,9 @@
3131

3232
import javax.inject.Named;
3333
import java.util.List;
34+
import java.util.Objects;
3435
import java.util.Optional;
36+
import java.util.stream.Collectors;
3537

3638
import static io.flamingock.internal.common.core.audit.AuditReaderType.MONGOCK;
3739
import static io.flamingock.internal.common.core.metadata.Constants.MONGOCK_IMPORT_EMPTY_ORIGIN_ALLOWED_PROPERTY_KEY;
@@ -61,7 +63,10 @@ public void importHistory(@Named("change.targetSystem.id") String targetSystemId
6163
logger.info("Starting audit log migration from Mongock to Flamingock community audit store");
6264
AuditHistoryReader legacyHistoryReader = getAuditHistoryReader(targetSystemId, targetSystemManager);
6365
PipelineHelper pipelineHelper = new PipelineHelper(pipelineDescriptor);
64-
List<AuditEntry> legacyHistory = legacyHistoryReader.getAuditHistory();
66+
List<AuditEntry> legacyHistory = legacyHistoryReader.getAuditHistory()
67+
.stream()
68+
.filter(Objects::nonNull)
69+
.collect(Collectors.toList());
6570
boolean ignoreUnknownEntries = resolveIgnoreUnknownEntries(ignoreUnknownEntriesPropertyValue);
6671
validate(legacyHistory, targetSystemId, emptyOriginAllowedPropertyValue);
6772
legacyHistory.forEach(auditEntryFromOrigin -> {

0 commit comments

Comments
 (0)