diff --git a/legacy/mongock-importer-mongodb/src/main/java/io/flamingock/importer/mongock/mongodb/MongockAuditEntry.java b/legacy/mongock-importer-mongodb/src/main/java/io/flamingock/importer/mongock/mongodb/MongockAuditEntry.java index 5ef48e6e2..1daa1cf65 100644 --- a/legacy/mongock-importer-mongodb/src/main/java/io/flamingock/importer/mongock/mongodb/MongockAuditEntry.java +++ b/legacy/mongock-importer-mongodb/src/main/java/io/flamingock/importer/mongock/mongodb/MongockAuditEntry.java @@ -56,14 +56,14 @@ public MongockAuditEntry(String executionId, this.author = author; this.timestamp = timestamp; this.state = MongockAuditEntry.MongockChangeState.valueOf(state); - this.type = MongockChangeType.valueOf(type); + this.type = parseType(type); this.changeLogClass = changeLogClass; this.changeSetMethod = changeSetMethod; this.metadata = metadata; this.executionMillis = executionMillis; this.executionHostname = executionHostname; this.errorTrace = errorTrace; - this.systemChange = systemChange; + this.systemChange = normalizeSystemChange(systemChange); this.originalTimestamp = originalTimestamp; } @@ -112,7 +112,7 @@ public AuditEntry.ChangeType getType() { } public void setType(String type) { - this.type = MongockChangeType.valueOf(type); + this.type = parseType(type); } public String getChangeLogClass() { @@ -168,7 +168,7 @@ public Boolean getSystemChange() { } public void setSystemChange(Boolean systemChange) { - this.systemChange = systemChange; + this.systemChange = normalizeSystemChange(systemChange); } public Date getOriginalTimestamp() { @@ -183,6 +183,17 @@ public boolean shouldBeIgnored() { return state == MongockAuditEntry.MongockChangeState.IGNORED; } + private static MongockChangeType parseType(String type) { + if (type == null || type.trim().isEmpty()) { + return MongockChangeType.EXECUTION; + } + return MongockChangeType.valueOf(type); + } + + private static Boolean normalizeSystemChange(Boolean systemChange) { + return systemChange != null ? systemChange : Boolean.FALSE; + } + public enum MongockChangeState { EXECUTED, FAILED, ROLLED_BACK, ROLLBACK_FAILED, IGNORED; diff --git a/legacy/mongock-importer-mongodb/src/test/java/io/flamingock/importer/mongock/mongodb/MongoDBImporterTest.java b/legacy/mongock-importer-mongodb/src/test/java/io/flamingock/importer/mongock/mongodb/MongoDBImporterTest.java index 3c48a8bd1..fa4fad124 100644 --- a/legacy/mongock-importer-mongodb/src/test/java/io/flamingock/importer/mongock/mongodb/MongoDBImporterTest.java +++ b/legacy/mongock-importer-mongodb/src/test/java/io/flamingock/importer/mongock/mongodb/MongoDBImporterTest.java @@ -22,9 +22,13 @@ import com.mongodb.client.MongoDatabase; import io.flamingock.api.annotations.EnableFlamingock; import io.flamingock.api.annotations.Stage; +import io.flamingock.common.test.mongock.MongockChangeEntry; +import io.flamingock.common.test.mongock.MongockChangeState; +import io.flamingock.common.test.mongock.MongockTestHelper; import io.flamingock.store.mongodb.sync.MongoDBSyncAuditStore; import io.flamingock.core.kit.TestKit; import io.flamingock.core.kit.audit.AuditTestHelper; +import io.flamingock.internal.common.core.audit.AuditEntry; import io.flamingock.internal.common.core.response.data.ErrorInfo; import io.flamingock.internal.core.operation.StagedExecuteOperationException; import io.flamingock.internal.core.builder.runner.Runner; @@ -47,12 +51,16 @@ import static io.flamingock.core.kit.audit.AuditEntryExpectation.APPLIED; import static io.flamingock.core.kit.audit.AuditEntryExpectation.STARTED; +import static io.flamingock.core.kit.audit.AuditEntryExpectation.auditEntry; import static io.flamingock.internal.common.core.metadata.Constants.DEFAULT_MONGOCK_ORIGIN; import static io.flamingock.internal.common.core.metadata.Constants.MONGOCK_IMPORT_EMPTY_ORIGIN_ALLOWED_PROPERTY_KEY; import static io.flamingock.internal.common.core.metadata.Constants.MONGOCK_IMPORT_IGNORE_UNKNOWN_ENTRIES_PROPERTY_KEY; import static io.flamingock.internal.common.core.metadata.Constants.MONGOCK_IMPORT_ORIGIN_PROPERTY_KEY; import static io.flamingock.internal.common.core.metadata.Constants.MONGOCK_IMPORT_SKIP_PROPERTY_KEY; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; @Testcontainers @@ -373,6 +381,52 @@ void GIVEN_allMongockChangeUnitsAlreadyExecutedAndCustomOriginProvidedByLiteralV Assertions.assertEquals("readonly", users.get(1).getList("roles", String.class).get(0)); } + @Test + @DisplayName("GIVEN Mongock v4 style audit entries without type, errorTrace and systemChange " + + "WHEN migrating to Flamingock Community " + + "THEN should import the history using v4 compatibility defaults") + void GIVEN_mongockV4StyleAuditEntries_WHEN_migratingToFlamingockCommunity_THEN_shouldImportWithCompatibilityDefaults() { + mongockTestHelper.writeAll(buildMongockV4ExecutedEntries()); + + MongoDBSyncTargetSystem mongodbTargetSystem = new MongoDBSyncTargetSystem("mongodb-target-system", mongoClient, DATABASE_NAME); + + Runner flamingock = testKit.createBuilder() + .addTargetSystem(mongodbTargetSystem) + .build(); + + flamingock.run(); + + auditHelper.verifyAuditSequenceStrict( + auditEntry().withChangeId("mongock-change-1") + .withState(AuditEntry.Status.APPLIED) + .withType(AuditEntry.ChangeType.MONGOCK_EXECUTION) + .withSystemChange(false), + auditEntry().withChangeId("mongock-change-2") + .withState(AuditEntry.Status.APPLIED) + .withType(AuditEntry.ChangeType.MONGOCK_EXECUTION) + .withSystemChange(false), + STARTED("migration-mongock-to-flamingock-community"), + APPLIED("migration-mongock-to-flamingock-community"), + STARTED("create-users-collection-with-index"), + APPLIED("create-users-collection-with-index"), + STARTED("seed-users"), + APPLIED("seed-users") + ); + + AuditEntry importedChange1 = getAuditEntryByChangeId("mongock-change-1"); + AuditEntry importedChange2 = getAuditEntryByChangeId("mongock-change-2"); + + assertNotNull(importedChange1); + assertEquals(AuditEntry.ChangeType.MONGOCK_EXECUTION, importedChange1.getType()); + assertFalse(importedChange1.getSystemChange()); + assertNull(importedChange1.getErrorTrace()); + + assertNotNull(importedChange2); + assertEquals(AuditEntry.ChangeType.MONGOCK_EXECUTION, importedChange2.getType()); + assertFalse(importedChange2.getSystemChange()); + assertNull(importedChange2.getErrorTrace()); + } + @Test @DisplayName("GIVEN Mongock audit history contains unknown entries " + "AND relaxed import flag is not provided " + @@ -496,6 +550,54 @@ private static String firstFailedStageErrorMessage(StagedExecuteOperationExcepti .orElseThrow(() -> new AssertionError("Expected a failed stage with ErrorInfo")); } + private List buildMongockV4ExecutedEntries() { + try { + List entries = new ArrayList<>(); + entries.add(new MongockChangeEntry( + "v4-execution-1", + "mongock-change-1", + "mongock", + MongockTestHelper.DEFAULT_DATE_FORMAT.parse("2025-06-19T05:43:57.132Z"), + MongockChangeState.EXECUTED, + null, + "io.mongock.examples.mongodb.standalone.mondogb.sync.migration.initializer.ClientInitializerChangeUnit", + "apply", + null, + 23L, + MongockTestHelper.DEFAULT_HOSTNAME, + null, + null, + null + )); + entries.add(new MongockChangeEntry( + "v4-execution-1", + "mongock-change-2", + "mongock", + MongockTestHelper.DEFAULT_DATE_FORMAT.parse("2025-06-19T05:43:57.169Z"), + MongockChangeState.EXECUTED, + null, + "io.mongock.examples.mongodb.standalone.mondogb.sync.migration.updater.ClientUpdaterChangeUnit", + "apply", + null, + 20L, + MongockTestHelper.DEFAULT_HOSTNAME, + null, + null, + null + )); + return entries; + } catch (Exception e) { + throw new RuntimeException("Failed to build Mongock v4 test entries", e); + } + } + + private AuditEntry getAuditEntryByChangeId(String changeId) { + return auditHelper.getAuditEntriesSorted().stream() + .filter(entry -> changeId.equals(entry.getChangeId())) + .findFirst() + .orElse(null); + } + @Test @DisplayName("GIVEN all Mongock changeUnits already executed " + diff --git a/legacy/mongock-importer-mongodb/src/test/java/io/flamingock/importer/mongock/mongodb/MongoDBMongockTestHelper.java b/legacy/mongock-importer-mongodb/src/test/java/io/flamingock/importer/mongock/mongodb/MongoDBMongockTestHelper.java index 28aab551c..9e936ccce 100644 --- a/legacy/mongock-importer-mongodb/src/test/java/io/flamingock/importer/mongock/mongodb/MongoDBMongockTestHelper.java +++ b/legacy/mongock-importer-mongodb/src/test/java/io/flamingock/importer/mongock/mongodb/MongoDBMongockTestHelper.java @@ -55,16 +55,22 @@ private Document convertToDocument(MongockChangeEntry entry) { document.put("changeId", entry.getChangeId()); document.put("author", entry.getAuthor()); document.put("timestamp", entry.getTimestamp()); - document.put("state", entry.getState() != null ? entry.getState().toString() : null); - document.put("type", entry.getType() != null ? entry.getType().toString() : null); - document.put("changeLogClass", entry.getChangeLogClass()); - document.put("changeSetMethod", entry.getChangeSetMethod()); - document.put("metadata", entry.getMetadata()); + putIfNotNull(document, "state", entry.getState() != null ? entry.getState().toString() : null); + putIfNotNull(document, "type", entry.getType() != null ? entry.getType().toString() : null); + putIfNotNull(document, "changeLogClass", entry.getChangeLogClass()); + putIfNotNull(document, "changeSetMethod", entry.getChangeSetMethod()); + putIfNotNull(document, "metadata", entry.getMetadata()); document.put("executionMillis", entry.getExecutionMillis()); - document.put("executionHostname", entry.getExecutionHostname()); - document.put("errorTrace", entry.getErrorTrace()); - document.put("systemChange", entry.getSystemChange()); - document.put("originalTimestamp", entry.getOriginalTimestamp()); + putIfNotNull(document, "executionHostname", entry.getExecutionHostname()); + putIfNotNull(document, "errorTrace", entry.getErrorTrace()); + putIfNotNull(document, "systemChange", entry.getSystemChange()); + putIfNotNull(document, "originalTimestamp", entry.getOriginalTimestamp()); return document; } + + private void putIfNotNull(Document document, String key, Object value) { + if (value != null) { + document.put(key, value); + } + } }