Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,25 +15,19 @@
*/
package io.flamingock.internal.core.task.loaded;

import io.flamingock.api.annotations.Categories;
import io.flamingock.api.task.ChangeCategory;
import io.flamingock.internal.common.core.error.validation.ValidationError;
import io.flamingock.internal.core.pipeline.loaded.stage.StageValidationContext;
import io.flamingock.internal.util.ReflectionUtil;
import io.flamingock.internal.common.core.task.RecoveryDescriptor;
import io.flamingock.internal.common.core.task.TargetSystemDescriptor;
import org.jetbrains.annotations.NotNull;
import io.flamingock.internal.core.pipeline.loaded.stage.StageValidationContext;
import io.flamingock.internal.util.log.FlamingockLoggerFactory;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;

import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

public abstract class AbstractLoadedChange extends AbstractReflectionLoadedTask {

Expand Down Expand Up @@ -97,19 +91,19 @@ public List<ValidationError> getValidationErrors(StageValidationContext context)
@NotNull
private Optional<ValidationError> getOrderError(StageValidationContext context) {
String order = getOrder().orElse(null);
if(context.getSortType().isSorted()) {
if (context.getSortType().isSorted()) {
if (order == null || order.isEmpty()) {
return Optional.of(new ValidationError("Change in a sorted stage but no order value was provided", id, "change"));
}

if(context.getSortType() == StageValidationContext.SortType.SEQUENTIAL_FORMATTED) {
if (context.getSortType() == StageValidationContext.SortType.SEQUENTIAL_FORMATTED) {
if (!ORDER_PATTERN.matcher(order).matches()) {
String message = String.format("Invalid order field format in change[%s]. Order must match pattern: %s", id, ORDER_REG_EXP);
return Optional.of(new ValidationError(message, id, "task"));
}
}

} else if(order != null) {
} else if (order != null) {
logger.warn("Change[{}] is in an auto-sorted stage but order value was provided - order will be ignored and managed automatically by Flamingock", id);

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import io.flamingock.api.annotations.EnableFlamingock;
import io.flamingock.api.annotations.Stage;
import io.flamingock.community.couchbase.driver.CouchbaseAuditStore;
import io.flamingock.internal.common.core.error.FlamingockException;
import io.flamingock.internal.common.couchbase.CouchbaseCollectionHelper;
import io.flamingock.internal.core.builder.FlamingockFactory;
import io.flamingock.internal.core.runner.Runner;
Expand All @@ -33,6 +34,7 @@
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.testcontainers.couchbase.CouchbaseContainer;
import org.testcontainers.junit.jupiter.Container;
Expand All @@ -41,10 +43,14 @@
import java.time.Duration;
import java.time.Instant;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static io.flamingock.internal.common.core.metadata.Constants.DEFAULT_MONGOCK_ORIGIN;
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.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

@Testcontainers
Expand Down Expand Up @@ -99,24 +105,15 @@ void cleanUp() {
}

@Test
void testImporterIntegration() {
@DisplayName("GIVEN all Mongock changeUnits already executed" +
"WHEN migrating to Flamingock Community " +
"THEN should import the entire history " +
"AND execute the pending flamingock changes")
void GIVEN_allMongockChangeUnitsAlreadyExecuted_WHEN_migratingToFlamingockCommunity_THEN_shouldImportEntireHistory() {
Collection originCollection = cluster.bucket(MONGOCK_BUCKET_NAME).scope(MONGOCK_SCOPE_NAME).collection(MONGOCK_COLLECTION_NAME);
JsonObject doc = JsonObject.create()
.put("executionId", "exec-1")
.put("changeId", "change-1")
.put("author", "author1")
.put("timestamp", String.valueOf(Instant.now().toEpochMilli()))
.put("state", "EXECUTED")
.put("type", "EXECUTION")
.put("changeLogClass", "io.flamingock.changelog.Class1")
.put("changeSetMethod", "method1")
.putNull("metadata")
.put("executionMillis", 123L)
.put("executionHostName", "host1")
.putNull("errorTrace")
.put("systemChange", true)
.put("_doctype", "mongockChangeEntry");
originCollection.upsert("change-1", doc);

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);

Expand All @@ -129,34 +126,104 @@ void testImporterIntegration() {

flamingock.run();

List<JsonObject> auditLog = CouchbaseCollectionHelper.selectAllDocuments(cluster, FLAMINGOCK_BUCKET_NAME, FLAMINGOCK_SCOPE_NAME, FLAMINGOCK_COLLECTION_NAME);
validateFlamingockAuditOutput();

assertFalse(auditLog.isEmpty(), "Audit log should not be empty");
}

JsonObject entry = auditLog.stream()
.filter(e -> "change-1".equals(e.getString("changeId")))
.findFirst()
.orElseThrow(() -> new AssertionError("Entry with changeId 'change-1' not found"));
@Test
@DisplayName("GIVEN some Mongock changeUnits already executed " +
"AND some other Mongock changeUnits pending for execution" +
"WHEN migrating to Flamingock Community" +
"THEN migrates the history with the executed changeUnits " +
"AND executes the pending Mongock changeUnits " +
"AND executes the pending Flamingock changes")
void GIVEN_someChangeUnitsAlreadyExecuted_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"));

CouchbaseTargetSystem targetSystem = new CouchbaseTargetSystem("couchbase-target-system", cluster, FLAMINGOCK_BUCKET_NAME);

Runner flamingock = FlamingockFactory.getCommunityBuilder()
.setAuditStore(new CouchbaseAuditStore(cluster, FLAMINGOCK_BUCKET_NAME)
.withScopeName(FLAMINGOCK_SCOPE_NAME)
.withAuditRepositoryName(FLAMINGOCK_COLLECTION_NAME))
.addTargetSystem(targetSystem)
.build();

assertEquals("change-1", entry.getString("changeId"));
assertEquals("author1", entry.getString("author"));
assertEquals("exec-1", entry.getString("executionId"));
assertEquals("APPLIED", entry.getString("state"));
assertTrue(entry.getBoolean("systemChange"));
flamingock.run();

validateFlamingockAuditOutput();
}

@Test
@Disabled("restore when https://trello.com/c/4gEQ8Wb4/458-mongock-legacy-targetsystem done")
void failIfEmptyOrigin() {
@DisplayName("GIVEN mongock audit history empty " +
"WHEN migrating to Flamingock Community" +
"THEN should throw exception")
void GIVEN_mongockAuditHistoryEmpty_WHEN_migratingToFlamingockCommunity_THEN_shouldThrowException() {
// Setup Mongock entries

CouchbaseTargetSystem targetSystem = new CouchbaseTargetSystem("couchbase-target-system", cluster, FLAMINGOCK_BUCKET_NAME);

Runner flamingock = FlamingockFactory.getCommunityBuilder()
.setAuditStore(new CouchbaseAuditStore(cluster, FLAMINGOCK_BUCKET_NAME)
.withScopeName(FLAMINGOCK_SCOPE_NAME)
.withAuditRepositoryName(FLAMINGOCK_COLLECTION_NAME))
.addTargetSystem(targetSystem)
.build();

org.junit.jupiter.api.Assertions.assertThrows(
io.flamingock.internal.common.core.error.FlamingockException.class,
flamingock::run
);
FlamingockException ex = assertThrows(FlamingockException.class, flamingock::run);
assertEquals("No audit entries found when importing from 'couchbase-target-system'.", ex.getMessage());

}


private static void validateFlamingockAuditOutput() {
List<JsonObject> auditLog = CouchbaseCollectionHelper.selectAllDocuments(cluster, FLAMINGOCK_BUCKET_NAME, FLAMINGOCK_SCOPE_NAME, FLAMINGOCK_COLLECTION_NAME);


Map<String, JsonObject> byChangeId = auditLog.stream()
.collect(Collectors.toMap(
e -> e.getString("changeId"),
e -> e
));


assertEquals(4, auditLog.size());
JsonObject importAudit = byChangeId.get("migration-mongock-to-flamingock-community");
JsonObject mongockAudit1 = byChangeId.get("mongock-change-1");
JsonObject mongockAudit2 = byChangeId.get("mongock-change-2");
JsonObject flamingockAudit = byChangeId.get("flamingock-change");
assertNotNull(importAudit);
assertEquals("APPLIED", importAudit.getString("state"));

assertNotNull(mongockAudit1);
assertEquals("APPLIED", mongockAudit1.getString("state"));

assertNotNull(mongockAudit2);
assertEquals("APPLIED", mongockAudit2.getString("state"));

assertNotNull(flamingockAudit);
assertEquals("APPLIED", flamingockAudit.getString("state"));
}

private static JsonObject createAuditObject(String value) {
JsonObject doc = JsonObject.create()
.put("executionId", "exec-1")
.put("changeId", value)
.put("author", "author1")
.put("timestamp", String.valueOf(Instant.now().toEpochMilli()))
.put("state", "EXECUTED")
.put("type", "EXECUTION")
.put("changeLogClass", "io.flamingock.changelog.Class1")
.put("changeSetMethod", "method1")
.putNull("metadata")
.put("executionMillis", 123L)
.put("executionHostName", "host1")
.putNull("errorTrace")
.put("systemChange", true)
.put("_doctype", "mongockChangeEntry");
return doc;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
import io.mongock.api.annotations.ChangeUnit;
import io.mongock.api.annotations.Execution;

@ChangeUnit(id = "client-initializer", order = "1", author = "flamingock-team", transactional = false)
@ChangeUnit(id = "mongock-change-1", order = "1", author = "flamingock-team", transactional = false)
public class MongockChange1 {

@Execution
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
import io.mongock.api.annotations.ChangeUnit;
import io.mongock.api.annotations.Execution;

@ChangeUnit(id = "client-updater", order = "2", author = "flamingock-team", transactional = false)
@ChangeUnit(id = "mongock-change-2", order = "2", author = "flamingock-team", transactional = false)
public class MongockChange2 {

@Execution
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@
import java.util.Collections;

@TargetSystem(id = "couchbase-target-system")
@Change(id = "create-users-collection", author = "importer", transactional = false)
public class _0001__CreateUsersCollectionChange {
@Change(id = "flamingock-change", author = "importer", transactional = false)
public class _0003__Flamingock_change {

@Apply
public void apply(Bucket bucket) {
Expand Down
Loading
Loading