diff --git a/core/flamingock-core-commons/src/main/java/io/flamingock/internal/common/core/metadata/Constants.java b/core/flamingock-core-commons/src/main/java/io/flamingock/internal/common/core/metadata/Constants.java index 07e327669..019acd5a9 100644 --- a/core/flamingock-core-commons/src/main/java/io/flamingock/internal/common/core/metadata/Constants.java +++ b/core/flamingock-core-commons/src/main/java/io/flamingock/internal/common/core/metadata/Constants.java @@ -23,6 +23,7 @@ public final class Constants { public static final String DEFAULT_MONGOCK_ORIGIN = "mongockChangeLog"; + public static final String MONGOCK_IMPORT_SKIP_PROPERTY_KEY = "internal.mongock.import.skip"; public static final String MONGOCK_IMPORT_ORIGIN_PROPERTY_KEY = "internal.mongock.import.origin"; public static final String MONGOCK_IMPORT_EMPTY_ORIGIN_ALLOWED_PROPERTY_KEY = "internal.mongock.import.emptyOriginAllowed"; diff --git a/legacy/mongock-importer-couchbase/src/test/java/io/flamingock/importer/mongock/couchbase/CouchbaseImporterTest.java b/legacy/mongock-importer-couchbase/src/test/java/io/flamingock/importer/mongock/couchbase/CouchbaseImporterTest.java index 513e87d60..2b8a93c35 100644 --- a/legacy/mongock-importer-couchbase/src/test/java/io/flamingock/importer/mongock/couchbase/CouchbaseImporterTest.java +++ b/legacy/mongock-importer-couchbase/src/test/java/io/flamingock/importer/mongock/couchbase/CouchbaseImporterTest.java @@ -48,6 +48,7 @@ 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_ORIGIN_PROPERTY_KEY; +import static io.flamingock.internal.common.core.metadata.Constants.MONGOCK_IMPORT_SKIP_PROPERTY_KEY; import static io.flamingock.internal.util.constants.AuditEntryFieldConstants.KEY_CREATED_AT; import static io.flamingock.internal.util.constants.AuditEntryFieldConstants.KEY_STATE; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -352,6 +353,183 @@ void GIVEN_allMongockChangeUnitsAlreadyExecutedAndCustomOriginProvided_WHEN_migr assertEquals("APPLIED", auditLog.get(6).getString("state")); } + @Test + @DisplayName("GIVEN skip import flag with invalid value " + + "WHEN migrating to Flamingock Community" + + "THEN should throw exception") + void GIVEN_skipImportFlagWithInvalidValue_WHEN_migratingToFlamingockCommunity_THEN_shouldThrowException() { + + final String SKIP_IMPORT_VALUE = "invalid_value"; + + CouchbaseTargetSystem targetSystem = new CouchbaseTargetSystem("couchbase-target-system", cluster, FLAMINGOCK_BUCKET_NAME); + + Runner flamingock = FlamingockFactory.getCommunityBuilder() + .setAuditStore(CouchbaseAuditStore.from(targetSystem) + .withScopeName(FLAMINGOCK_SCOPE_NAME) + .withAuditRepositoryName(FLAMINGOCK_COLLECTION_NAME)) + .addTargetSystem(targetSystem) + .setProperty(MONGOCK_IMPORT_SKIP_PROPERTY_KEY, SKIP_IMPORT_VALUE) // only allows empty / true / false + .build(); + + FlamingockException ex = assertThrows(FlamingockException.class, flamingock::run); + assertEquals("Invalid value for " + MONGOCK_IMPORT_SKIP_PROPERTY_KEY + ": " + SKIP_IMPORT_VALUE + + " (expected \"true\" or \"false\" or empty)", ex.getMessage()); + } + + @Test + @DisplayName("GIVEN all Mongock changeUnits already executed " + + "AND skip import flag enabled " + + "WHEN migrating to Flamingock Community" + + "THEN should not import any audit history entry " + + "AND execute the all mongock and flamingock changes") + void GIVEN_skipImportFlagEnabled_WHEN_migratingToFlamingockCommunity_THEN_shouldNotMigrateAnyAuditLog() { + + Collection originCollection = cluster.bucket(MONGOCK_BUCKET_NAME).scope(MONGOCK_SCOPE_NAME).collection(MONGOCK_COLLECTION_NAME); + + originCollection.upsert("mongock-change-1", createAuditObject("mongock-change-1")); + originCollection.upsert("mongock-change-2", createAuditObject("mongock-change-2")); + + final String SKIP_IMPORT_VALUE = "true"; + + CouchbaseTargetSystem targetSystem = new CouchbaseTargetSystem("couchbase-target-system", cluster, FLAMINGOCK_BUCKET_NAME); + + Runner flamingock = FlamingockFactory.getCommunityBuilder() + .setAuditStore(CouchbaseAuditStore.from(targetSystem) + .withScopeName(FLAMINGOCK_SCOPE_NAME) + .withAuditRepositoryName(FLAMINGOCK_COLLECTION_NAME)) + .addTargetSystem(targetSystem) + .setProperty(MONGOCK_IMPORT_SKIP_PROPERTY_KEY, SKIP_IMPORT_VALUE) // only allows empty / true / false + .build(); + + flamingock.run(); + + List auditLog = getAuditLog(); + + assertEquals(8, auditLog.size()); + + assertEquals("migration-mongock-to-flamingock-community", auditLog.get(0).getString("changeId")); + assertEquals("STARTED", auditLog.get(0).getString("state")); + + assertEquals("migration-mongock-to-flamingock-community", auditLog.get(1).getString("changeId")); + assertEquals("APPLIED", auditLog.get(1).getString("state")); + + assertEquals("mongock-change-1", auditLog.get(2).getString("changeId")); + assertEquals("STARTED", auditLog.get(2).getString("state")); + + assertEquals("mongock-change-1", auditLog.get(3).getString("changeId")); + assertEquals("APPLIED", auditLog.get(3).getString("state")); + + assertEquals("mongock-change-2", auditLog.get(4).getString("changeId")); + assertEquals("STARTED", auditLog.get(4).getString("state")); + + assertEquals("mongock-change-2", auditLog.get(5).getString("changeId")); + assertEquals("APPLIED", auditLog.get(5).getString("state")); + + assertEquals("flamingock-change", auditLog.get(6).getString("changeId")); + assertEquals("STARTED", auditLog.get(6).getString("state")); + + assertEquals("flamingock-change", auditLog.get(7).getString("changeId")); + assertEquals("APPLIED", auditLog.get(7).getString("state")); + } + + @Test + @DisplayName("GIVEN all Mongock changeUnits already executed " + + "AND skip import flag disabled (explicit) " + + "WHEN migrating to Flamingock Community " + + "THEN should import the entire history " + + "AND execute the pending flamingock changes") + void GIVEN_allMongockChangeUnitsAlreadyExecutedAndSkipImportFlagDisabledExplicit_WHEN_migratingToFlamingockCommunity_THEN_shouldImportEntireHistory() { + Collection originCollection = cluster.bucket(MONGOCK_BUCKET_NAME).scope(MONGOCK_SCOPE_NAME).collection(MONGOCK_COLLECTION_NAME); + + originCollection.upsert("mongock-change-1", createAuditObject("mongock-change-1")); + originCollection.upsert("mongock-change-2", createAuditObject("mongock-change-2")); + + CouchbaseTargetSystem targetSystem = new CouchbaseTargetSystem("couchbase-target-system", cluster, FLAMINGOCK_BUCKET_NAME); + + final String SKIP_IMPORT_VALUE = "false"; + + Runner flamingock = FlamingockFactory.getCommunityBuilder() + .setAuditStore(CouchbaseAuditStore.from(targetSystem) + .withScopeName(FLAMINGOCK_SCOPE_NAME) + .withAuditRepositoryName(FLAMINGOCK_COLLECTION_NAME)) + .addTargetSystem(targetSystem) + .setProperty(MONGOCK_IMPORT_SKIP_PROPERTY_KEY, SKIP_IMPORT_VALUE) // only allows empty / true / false + .build(); + + flamingock.run(); + + List auditLog = getAuditLog(); + + assertEquals(6, auditLog.size()); + + assertEquals("mongock-change-1", auditLog.get(0).getString("changeId")); + assertEquals("APPLIED", auditLog.get(0).getString("state")); + + assertEquals("mongock-change-2", auditLog.get(1).getString("changeId")); + assertEquals("APPLIED", auditLog.get(1).getString("state")); + + assertEquals("migration-mongock-to-flamingock-community", auditLog.get(2).getString("changeId")); + assertEquals("STARTED", auditLog.get(2).getString("state")); + + assertEquals("migration-mongock-to-flamingock-community", auditLog.get(3).getString("changeId")); + assertEquals("APPLIED", auditLog.get(3).getString("state")); + + assertEquals("flamingock-change", auditLog.get(4).getString("changeId")); + assertEquals("STARTED", auditLog.get(4).getString("state")); + + assertEquals("flamingock-change", auditLog.get(5).getString("changeId")); + assertEquals("APPLIED", auditLog.get(5).getString("state")); + } + + @Test + @DisplayName("GIVEN all Mongock changeUnits already executed " + + "AND skip import flag disabled (implicit) " + + "WHEN migrating to Flamingock Community " + + "THEN should import the entire history " + + "AND execute the pending flamingock changes") + void GIVEN_allMongockChangeUnitsAlreadyExecutedAndSkipImportFlagDisabledImplicit_WHEN_migratingToFlamingockCommunity_THEN_shouldImportEntireHistory() { + Collection originCollection = cluster.bucket(MONGOCK_BUCKET_NAME).scope(MONGOCK_SCOPE_NAME).collection(MONGOCK_COLLECTION_NAME); + + originCollection.upsert("mongock-change-1", createAuditObject("mongock-change-1")); + originCollection.upsert("mongock-change-2", createAuditObject("mongock-change-2")); + + CouchbaseTargetSystem targetSystem = new CouchbaseTargetSystem("couchbase-target-system", cluster, FLAMINGOCK_BUCKET_NAME); + + final String SKIP_IMPORT_VALUE = ""; + + Runner flamingock = FlamingockFactory.getCommunityBuilder() + .setAuditStore(CouchbaseAuditStore.from(targetSystem) + .withScopeName(FLAMINGOCK_SCOPE_NAME) + .withAuditRepositoryName(FLAMINGOCK_COLLECTION_NAME)) + .addTargetSystem(targetSystem) + .setProperty(MONGOCK_IMPORT_SKIP_PROPERTY_KEY, SKIP_IMPORT_VALUE) // only allows empty / true / false + .build(); + + flamingock.run(); + + List auditLog = getAuditLog(); + + assertEquals(6, auditLog.size()); + + assertEquals("mongock-change-1", auditLog.get(0).getString("changeId")); + assertEquals("APPLIED", auditLog.get(0).getString("state")); + + assertEquals("mongock-change-2", auditLog.get(1).getString("changeId")); + assertEquals("APPLIED", auditLog.get(1).getString("state")); + + assertEquals("migration-mongock-to-flamingock-community", auditLog.get(2).getString("changeId")); + assertEquals("STARTED", auditLog.get(2).getString("state")); + + assertEquals("migration-mongock-to-flamingock-community", auditLog.get(3).getString("changeId")); + assertEquals("APPLIED", auditLog.get(3).getString("state")); + + assertEquals("flamingock-change", auditLog.get(4).getString("changeId")); + assertEquals("STARTED", auditLog.get(4).getString("state")); + + assertEquals("flamingock-change", auditLog.get(5).getString("changeId")); + assertEquals("APPLIED", auditLog.get(5).getString("state")); + } + private List getAuditLog() { return CouchbaseCollectionHelper.selectAllDocuments(cluster, FLAMINGOCK_BUCKET_NAME, FLAMINGOCK_SCOPE_NAME, FLAMINGOCK_COLLECTION_NAME) @@ -379,5 +557,4 @@ private static JsonObject createAuditObject(String value) { .put("_doctype", "mongockChangeEntry"); return doc; } - } diff --git a/legacy/mongock-importer-dynamodb/src/test/java/io/flamingock/importer/mongock/dynamodb/DynamoDBImporterTest.java b/legacy/mongock-importer-dynamodb/src/test/java/io/flamingock/importer/mongock/dynamodb/DynamoDBImporterTest.java index dc7214891..08388f080 100644 --- a/legacy/mongock-importer-dynamodb/src/test/java/io/flamingock/importer/mongock/dynamodb/DynamoDBImporterTest.java +++ b/legacy/mongock-importer-dynamodb/src/test/java/io/flamingock/importer/mongock/dynamodb/DynamoDBImporterTest.java @@ -49,6 +49,7 @@ 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_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.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -335,6 +336,188 @@ void GIVEN_allMongockChangeUnitsAlreadyExecutedAndCustomOriginProvided_WHEN_migr + // Validate actual table creation + assertTrue(client.listTables().tableNames().contains("users"), "Users table should exist"); + + // Verify table structure + DescribeTableResponse tableDescription = client.describeTable( + DescribeTableRequest.builder().tableName("users").build() + ); + assertEquals("email", tableDescription.table().keySchema().get(0).attributeName()); + assertEquals(KeyType.HASH, tableDescription.table().keySchema().get(0).keyType()); + } + + @Test + @DisplayName("GIVEN skip import flag with invalid value " + + "WHEN migrating to Flamingock Community" + + "THEN should throw exception") + void GIVEN_skipImportFlagWithInvalidValue_WHEN_migratingToFlamingockCommunity_THEN_shouldThrowException() { + // Setup Mongock entries + + DynamoDBTargetSystem dynamodbTargetSystem = new DynamoDBTargetSystem("dynamodb-target-system", client); + + final String SKIP_IMPORT_VALUE = "invalid_value"; + + Runner flamingock = testKit.createBuilder() + .addTargetSystem(dynamodbTargetSystem) + .setProperty(MONGOCK_IMPORT_SKIP_PROPERTY_KEY, SKIP_IMPORT_VALUE) // only allows empty / true / false + .build(); + + FlamingockException ex = assertThrows(FlamingockException.class, flamingock::run); + assertEquals("Invalid value for " + MONGOCK_IMPORT_SKIP_PROPERTY_KEY + ": " + SKIP_IMPORT_VALUE + + " (expected \"true\" or \"false\" or empty)", ex.getMessage()); + + } + + @Test + @DisplayName("GIVEN all Mongock changeUnits already executed " + + "AND skip import flag enabled " + + "WHEN migrating to Flamingock Community" + + "THEN should not import any audit history entry " + + "AND execute the all mongock and flamingock changes") + void GIVEN_skipImportFlagEnabled_WHEN_migratingToFlamingockCommunity_THEN_shouldNotMigrateAnyAuditLog() { + + // Setup Mongock entries + mongockTestHelper.setupBasicScenario(); + + DynamoDBTargetSystem dynamodbTargetSystem = new DynamoDBTargetSystem("dynamodb-target-system", client); + + final String SKIP_IMPORT_VALUE = "true"; + + Runner flamingock = testKit.createBuilder() + .addTargetSystem(dynamodbTargetSystem) + .setProperty(MONGOCK_IMPORT_SKIP_PROPERTY_KEY, SKIP_IMPORT_VALUE) // only allows empty / true / false + .build(); + + flamingock.run(); + + // Verify audit sequence: 8 total entries as shown in actual execution + auditHelper.verifyAuditSequenceStrict( + // System stage - actual system importer change + STARTED("migration-mongock-to-flamingock-community"), + APPLIED("migration-mongock-to-flamingock-community"), + + // Legacy changes + STARTED("mongock-change-1"), + APPLIED("mongock-change-1"), + STARTED("mongock-change-2"), + APPLIED("mongock-change-2"), + + // Application stage - new changes created by templates + STARTED("create-users-table"), + APPLIED("create-users-table") + ); + + + + // Validate actual table creation + assertTrue(client.listTables().tableNames().contains("users"), "Users table should exist"); + + // Verify table structure + DescribeTableResponse tableDescription = client.describeTable( + DescribeTableRequest.builder().tableName("users").build() + ); + assertEquals("email", tableDescription.table().keySchema().get(0).attributeName()); + assertEquals(KeyType.HASH, tableDescription.table().keySchema().get(0).keyType()); + } + + @Test + @DisplayName("GIVEN all Mongock changeUnits already executed " + + "AND skip import flag disabled (explicit) " + + "WHEN migrating to Flamingock Community " + + "THEN should import the entire history " + + "AND execute the pending flamingock changes") + void GIVEN_allMongockChangeUnitsAlreadyExecutedAndSkipImportFlagDisabledExplicit_WHEN_migratingToFlamingockCommunity_THEN_shouldImportEntireHistory() { + + // Setup Mongock entries + mongockTestHelper.setupBasicScenario(); + + DynamoDBTargetSystem dynamodbTargetSystem = new DynamoDBTargetSystem("dynamodb-target-system", client); + + final String SKIP_IMPORT_VALUE = "false"; + + Runner flamingock = testKit.createBuilder() + .addTargetSystem(dynamodbTargetSystem) + .setProperty(MONGOCK_IMPORT_SKIP_PROPERTY_KEY, SKIP_IMPORT_VALUE) // only allows empty / true / false + .build(); + + flamingock.run(); + + // Verify audit sequence: 9 total entries as shown in actual execution + // Legacy imports only show APPLIED (imported from Mongock), new changes show STARTED+APPLIED + auditHelper.verifyAuditSequenceStrict( + // Legacy imports from Mongock (APPLIED only - no STARTED for imported changes) + APPLIED("system-change-00001_before"), + APPLIED("system-change-00001"), + APPLIED("mongock-change-1_before"), + APPLIED("mongock-change-1"), + APPLIED("mongock-change-2"), + + // System stage - actual system importer change + STARTED("migration-mongock-to-flamingock-community"), + APPLIED("migration-mongock-to-flamingock-community"), + + // Application stage - new changes created by templates + STARTED("create-users-table"), + APPLIED("create-users-table") + ); + + + + // Validate actual table creation + assertTrue(client.listTables().tableNames().contains("users"), "Users table should exist"); + + // Verify table structure + DescribeTableResponse tableDescription = client.describeTable( + DescribeTableRequest.builder().tableName("users").build() + ); + assertEquals("email", tableDescription.table().keySchema().get(0).attributeName()); + assertEquals(KeyType.HASH, tableDescription.table().keySchema().get(0).keyType()); + } + + @Test + @DisplayName("GIVEN all Mongock changeUnits already executed " + + "AND skip import flag disabled (implicit) " + + "WHEN migrating to Flamingock Community " + + "THEN should import the entire history " + + "AND execute the pending flamingock changes") + void GIVEN_allMongockChangeUnitsAlreadyExecutedAndSkipImportFlagDisabledImplicit_WHEN_migratingToFlamingockCommunity_THEN_shouldImportEntireHistory() { + + // Setup Mongock entries + mongockTestHelper.setupBasicScenario(); + + DynamoDBTargetSystem dynamodbTargetSystem = new DynamoDBTargetSystem("dynamodb-target-system", client); + + final String SKIP_IMPORT_VALUE = ""; + + Runner flamingock = testKit.createBuilder() + .addTargetSystem(dynamodbTargetSystem) + .setProperty(MONGOCK_IMPORT_SKIP_PROPERTY_KEY, SKIP_IMPORT_VALUE) // only allows empty / true / false + .build(); + + flamingock.run(); + + // Verify audit sequence: 9 total entries as shown in actual execution + // Legacy imports only show APPLIED (imported from Mongock), new changes show STARTED+APPLIED + auditHelper.verifyAuditSequenceStrict( + // Legacy imports from Mongock (APPLIED only - no STARTED for imported changes) + APPLIED("system-change-00001_before"), + APPLIED("system-change-00001"), + APPLIED("mongock-change-1_before"), + APPLIED("mongock-change-1"), + APPLIED("mongock-change-2"), + + // System stage - actual system importer change + STARTED("migration-mongock-to-flamingock-community"), + APPLIED("migration-mongock-to-flamingock-community"), + + // Application stage - new changes created by templates + STARTED("create-users-table"), + APPLIED("create-users-table") + ); + + + // Validate actual table creation assertTrue(client.listTables().tableNames().contains("users"), "Users table should exist"); 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 a14e0cf49..d369afecf 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 @@ -49,6 +49,7 @@ 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_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.assertThrows; @@ -353,6 +354,203 @@ void GIVEN_allMongockChangeUnitsAlreadyExecutedAndCustomOriginProvidedByLiteralV + // Validate actual change + List users = database.getCollection("users") + .find() + .into(new ArrayList<>()); + + assertEquals(2, users.size()); + Assertions.assertEquals("Admin", users.get(0).getString("name")); + Assertions.assertEquals("admin@company.com", users.get(0).getString("email")); + Assertions.assertEquals("superuser", users.get(0).getList("roles", String.class).get(0)); + + Assertions.assertEquals("Backup", users.get(1).getString("name")); + Assertions.assertEquals("backup@company.com", users.get(1).getString("email")); + Assertions.assertEquals("readonly", users.get(1).getList("roles", String.class).get(0)); + } + + @Test + @DisplayName("GIVEN skip import flag with invalid value " + + "WHEN migrating to Flamingock Community" + + "THEN should throw exception") + void GIVEN_skipImportFlagWithInvalidValue_WHEN_migratingToFlamingockCommunity_THEN_shouldThrowException() { + // Setup Mongock entries + + MongoDBSyncTargetSystem mongodbTargetSystem = new MongoDBSyncTargetSystem("mongodb-target-system", mongoClient, DATABASE_NAME); + + final String SKIP_IMPORT_VALUE = "invalid_value"; + + Runner flamingock = testKit.createBuilder() + .addTargetSystem(mongodbTargetSystem) + .setProperty(MONGOCK_IMPORT_SKIP_PROPERTY_KEY, SKIP_IMPORT_VALUE) // only allows empty / true / false + .build(); + + FlamingockException ex = assertThrows(FlamingockException.class, flamingock::run); + assertEquals("Invalid value for " + MONGOCK_IMPORT_SKIP_PROPERTY_KEY + ": " + SKIP_IMPORT_VALUE + + " (expected \"true\" or \"false\" or empty)", ex.getMessage()); + } + + + @Test + @DisplayName("GIVEN all Mongock changeUnits already executed " + + "AND skip import flag enabled " + + "WHEN migrating to Flamingock Community" + + "THEN should not import any audit history entry " + + "AND execute the all mongock and flamingock changes") + void GIVEN_skipImportFlagEnabled_WHEN_migratingToFlamingockCommunity_THEN_shouldNotMigrateAnyAuditLog() { + + // Setup Mongock entries + mongockTestHelper.setupBasicScenario(); + + MongoDBSyncTargetSystem mongodbTargetSystem = new MongoDBSyncTargetSystem("mongodb-target-system", mongoClient, DATABASE_NAME); + + final String SKIP_IMPORT_VALUE = "true"; + + Runner flamingock = testKit.createBuilder() + .addTargetSystem(mongodbTargetSystem) + .setProperty(MONGOCK_IMPORT_SKIP_PROPERTY_KEY, SKIP_IMPORT_VALUE) // only allows empty / true / false + .build(); + + flamingock.run(); + + // Verify audit sequence: 10 total entries as shown in actual execution + auditHelper.verifyAuditSequenceStrict( + // System stage - actual system importer change + STARTED("migration-mongock-to-flamingock-community"), + APPLIED("migration-mongock-to-flamingock-community"), + + // Legacy changes + STARTED("mongock-change-1"), + APPLIED("mongock-change-1"), + STARTED("mongock-change-2"), + APPLIED("mongock-change-2"), + + // Application stage - new changes created by templates + STARTED("create-users-collection-with-index"), + APPLIED("create-users-collection-with-index"), + STARTED("seed-users"), + APPLIED("seed-users") + ); + + + // Validate actual change + List users = database.getCollection("users") + .find() + .into(new ArrayList<>()); + + assertEquals(2, users.size()); + Assertions.assertEquals("Admin", users.get(0).getString("name")); + Assertions.assertEquals("admin@company.com", users.get(0).getString("email")); + Assertions.assertEquals("superuser", users.get(0).getList("roles", String.class).get(0)); + + Assertions.assertEquals("Backup", users.get(1).getString("name")); + Assertions.assertEquals("backup@company.com", users.get(1).getString("email")); + Assertions.assertEquals("readonly", users.get(1).getList("roles", String.class).get(0)); + } + + @Test + @DisplayName("GIVEN all Mongock changeUnits already executed " + + "AND skip import flag disabled (explicit) " + + "WHEN migrating to Flamingock Community " + + "THEN should import the entire history " + + "AND execute the pending flamingock changes") + void GIVEN_allMongockChangeUnitsAlreadyExecutedAndSkipImportFlagDisabledExplicit_WHEN_migratingToFlamingockCommunity_THEN_shouldImportEntireHistory() { + + // Setup Mongock entries + mongockTestHelper.setupBasicScenario(); + + MongoDBSyncTargetSystem mongodbTargetSystem = new MongoDBSyncTargetSystem("mongodb-target-system", mongoClient, DATABASE_NAME); + + final String SKIP_IMPORT_VALUE = "false"; + + Runner flamingock = testKit.createBuilder() + .addTargetSystem(mongodbTargetSystem) + .setProperty(MONGOCK_IMPORT_SKIP_PROPERTY_KEY, SKIP_IMPORT_VALUE) // only allows empty / true / false + .build(); + + flamingock.run(); + + // Verify audit sequence: 11 total entries as shown in actual execution + // Legacy imports only show APPLIED (imported from Mongock), new changes show STARTED+APPLIED + auditHelper.verifyAuditSequenceStrict( + // Legacy imports from Mongock (APPLIED only - no STARTED for imported changes) + APPLIED("system-change-00001_before"), + APPLIED("system-change-00001"), + APPLIED("mongock-change-1_before"), + APPLIED("mongock-change-1"), + APPLIED("mongock-change-2"), + + // System stage - actual system importer change + STARTED("migration-mongock-to-flamingock-community"), + APPLIED("migration-mongock-to-flamingock-community"), + + // Application stage - new changes created by templates + STARTED("create-users-collection-with-index"), + APPLIED("create-users-collection-with-index"), + STARTED("seed-users"), + APPLIED("seed-users") + ); + + + // Validate actual change + List users = database.getCollection("users") + .find() + .into(new ArrayList<>()); + + assertEquals(2, users.size()); + Assertions.assertEquals("Admin", users.get(0).getString("name")); + Assertions.assertEquals("admin@company.com", users.get(0).getString("email")); + Assertions.assertEquals("superuser", users.get(0).getList("roles", String.class).get(0)); + + Assertions.assertEquals("Backup", users.get(1).getString("name")); + Assertions.assertEquals("backup@company.com", users.get(1).getString("email")); + Assertions.assertEquals("readonly", users.get(1).getList("roles", String.class).get(0)); + } + + @Test + @DisplayName("GIVEN all Mongock changeUnits already executed " + + "AND skip import flag disabled (implicit) " + + "WHEN migrating to Flamingock Community " + + "THEN should import the entire history " + + "AND execute the pending flamingock changes") + void GIVEN_allMongockChangeUnitsAlreadyExecutedAndSkipImportFlagDisabledImplicit_WHEN_migratingToFlamingockCommunity_THEN_shouldImportEntireHistory() { + + // Setup Mongock entries + mongockTestHelper.setupBasicScenario(); + + MongoDBSyncTargetSystem mongodbTargetSystem = new MongoDBSyncTargetSystem("mongodb-target-system", mongoClient, DATABASE_NAME); + + final String SKIP_IMPORT_VALUE = ""; + + Runner flamingock = testKit.createBuilder() + .addTargetSystem(mongodbTargetSystem) + .setProperty(MONGOCK_IMPORT_SKIP_PROPERTY_KEY, SKIP_IMPORT_VALUE) // only allows empty / true / false + .build(); + + flamingock.run(); + + // Verify audit sequence: 11 total entries as shown in actual execution + // Legacy imports only show APPLIED (imported from Mongock), new changes show STARTED+APPLIED + auditHelper.verifyAuditSequenceStrict( + // Legacy imports from Mongock (APPLIED only - no STARTED for imported changes) + APPLIED("system-change-00001_before"), + APPLIED("system-change-00001"), + APPLIED("mongock-change-1_before"), + APPLIED("mongock-change-1"), + APPLIED("mongock-change-2"), + + // System stage - actual system importer change + STARTED("migration-mongock-to-flamingock-community"), + APPLIED("migration-mongock-to-flamingock-community"), + + // Application stage - new changes created by templates + STARTED("create-users-collection-with-index"), + APPLIED("create-users-collection-with-index"), + STARTED("seed-users"), + APPLIED("seed-users") + ); + + // Validate actual change List users = database.getCollection("users") .find() diff --git a/legacy/mongock-support/src/main/java/io/flamingock/support/mongock/MongockImportChange.java b/legacy/mongock-support/src/main/java/io/flamingock/support/mongock/MongockImportChange.java index 33a3454aa..2cdd278cf 100644 --- a/legacy/mongock-support/src/main/java/io/flamingock/support/mongock/MongockImportChange.java +++ b/legacy/mongock-support/src/main/java/io/flamingock/support/mongock/MongockImportChange.java @@ -23,7 +23,6 @@ import io.flamingock.internal.common.core.audit.AuditWriter; import io.flamingock.internal.common.core.error.FlamingockException; import io.flamingock.internal.common.core.pipeline.PipelineDescriptor; -import io.flamingock.internal.common.core.util.ConfigValueParser; import io.flamingock.internal.core.external.targets.TargetSystemManager; import io.flamingock.internal.core.external.targets.operations.TargetSystemOps; import io.flamingock.internal.core.external.targets.operations.TransactionalTargetSystemOps; @@ -35,6 +34,7 @@ import static io.flamingock.internal.common.core.audit.AuditReaderType.MONGOCK; 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_SKIP_PROPERTY_KEY; /** * This ChangeUnit is intentionally not annotated with @Change, @Apply, or similar, @@ -48,7 +48,12 @@ public void importHistory(@Named("change.targetSystem.id") String targetSystemId @NonLockGuarded TargetSystemManager targetSystemManager, @NonLockGuarded AuditWriter auditWriter, @NonLockGuarded PipelineDescriptor pipelineDescriptor, - @Nullable @Named(MONGOCK_IMPORT_EMPTY_ORIGIN_ALLOWED_PROPERTY_KEY) String emptyOriginAllowed) { + @Nullable @Named(MONGOCK_IMPORT_EMPTY_ORIGIN_ALLOWED_PROPERTY_KEY) String emptyOriginAllowed, + @Nullable @Named(MONGOCK_IMPORT_SKIP_PROPERTY_KEY) String skipImport) { + if (resolveSkipImport(skipImport)) { + logger.info("Mongock audit log import skipped (skipImport=true). No audit entries will be migrated."); + return; + } logger.info("Starting audit log migration from Mongock to Flamingock community audit store"); AuditHistoryReader legacyHistoryReader = getAuditHistoryReader(targetSystemId, targetSystemManager); PipelineHelper pipelineHelper = new PipelineHelper(pipelineDescriptor); @@ -102,4 +107,15 @@ private boolean resolveEmptyOriginAllowed(String raw) { throw new FlamingockException("Invalid value for " + MONGOCK_IMPORT_EMPTY_ORIGIN_ALLOWED_PROPERTY_KEY + ": " + raw + " (expected \"true\" or \"false\" or empty)"); } + + private boolean resolveSkipImport(String raw) { + if (raw == null || raw.trim().isEmpty()) { + return false; // default behaviour + } + String v = raw.trim(); + if ("true".equalsIgnoreCase(v)) return true; + if ("false".equalsIgnoreCase(v)) return false; + throw new FlamingockException("Invalid value for " + MONGOCK_IMPORT_SKIP_PROPERTY_KEY + ": " + raw + + " (expected \"true\" or \"false\" or empty)"); + } } diff --git a/legacy/mongock-support/src/main/java/io/flamingock/support/mongock/annotations/MongockSupport.java b/legacy/mongock-support/src/main/java/io/flamingock/support/mongock/annotations/MongockSupport.java index 6e5feeee7..22e12d71b 100644 --- a/legacy/mongock-support/src/main/java/io/flamingock/support/mongock/annotations/MongockSupport.java +++ b/legacy/mongock-support/src/main/java/io/flamingock/support/mongock/annotations/MongockSupport.java @@ -81,6 +81,20 @@ */ String targetSystem(); + /** + * Determines whether Mongock import should be skipped. + *

+ * Expected literal values are {@code "true"} or {@code "false"}. + *

+ * + *

+ * If empty (default), it will be treated as {@code "false"}. + *

+ * + * @return {@code "true"} to skip import, {@code "false"} to process it; empty treated as {@code "false"} + */ + String skipImport() default ""; + /** * Defines the origin collection/table name where Mongock audit entries are stored. *

diff --git a/legacy/mongock-support/src/main/java/io/flamingock/support/mongock/processor/MongockAnnotationProcessorPlugin.java b/legacy/mongock-support/src/main/java/io/flamingock/support/mongock/processor/MongockAnnotationProcessorPlugin.java index 43147d885..41c1c23d6 100644 --- a/legacy/mongock-support/src/main/java/io/flamingock/support/mongock/processor/MongockAnnotationProcessorPlugin.java +++ b/legacy/mongock-support/src/main/java/io/flamingock/support/mongock/processor/MongockAnnotationProcessorPlugin.java @@ -50,6 +50,7 @@ import java.util.stream.Stream; 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_SKIP_PROPERTY_KEY; import static io.flamingock.internal.common.core.metadata.Constants.MONGOCK_IMPORT_ORIGIN_PROPERTY_KEY; @SuppressWarnings("deprecation") @@ -84,6 +85,7 @@ public Collection findAnnotatedChanges() { .flatMap(List::stream) .filter(Objects::nonNull) .collect(Collectors.toList()); + changes.add(getImporterChange(mongockTargetSystemId)); return changes; @@ -142,16 +144,23 @@ private void processConfigurationProperties(MongockSupport mongockSupport, Map