All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.mongock.driver.mongodb.sync.v4.changelogs.LegacyService Maven / Gradle / Ivy

The newest version!
package io.mongock.driver.mongodb.sync.v4.changelogs;

import io.changock.migration.api.annotations.NonLockGuarded;
import io.changock.migration.api.annotations.NonLockGuardedType;
import io.mongock.api.config.LegacyMigration;
import io.mongock.api.config.LegacyMigrationMappingFields;
import io.mongock.driver.api.entry.ChangeEntry;
import io.mongock.driver.api.entry.ChangeEntryService;
import io.mongock.driver.api.entry.ChangeState;
import io.mongock.api.exception.MongockException;
import com.mongodb.client.FindIterable;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import io.mongock.driver.api.entry.ChangeType;
import io.mongock.utils.DateUtils;
import org.bson.Document;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.inject.Named;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.stream.Collectors;


public class LegacyService {

  private final static Logger logger = LoggerFactory.getLogger(LegacyService.class);

  public void executeMigration(@NonLockGuarded(NonLockGuardedType.NONE)
                               @Named("legacy-migration") LegacyMigration legacyMigration,
                               MongoDatabase mongoDatabase,
                               ChangeEntryService changeEntryService) {
    int changesMigrated = 0;
    Integer changesCountExpectation = legacyMigration.getChangesCountExpectation();
    if (changesCountExpectation == null) {
      logger.warn("[legacy-migration] - There is no changes count expectation!");
    }
    try {
      validateLegacyMigration(legacyMigration);
      List changesToMigrate = getOriginalMigrationAsChangeEntryList(mongoDatabase.getCollection(legacyMigration.getOrigin()), legacyMigration);
      Set allMigratedChanges = changeEntryService.getEntriesLog()
          .stream()
          .map(c -> String.format("%s-%s", c.getChangeId(), c.getAuthor()))
          .collect(Collectors.toSet());
      Set executedChanges = changeEntryService.getExecuted()
          .stream()
          .map(c -> String.format("%s-%s", c.getChangeId(), c.getAuthor()))
          .collect(Collectors.toSet());

      /**
       * For each change from the origin:
       * - if it's in the target and in executed state, it's fine. Nothing is done
       * - If it's in target but not in executed state, another changeEntry is inserted with the same (id,author), state= origin.state and date = NOW
       * - If it's not in target, another changeEntry is inserted with the same (id,author), state= origin.state and date = origin.date
       *
       * Explanation:
       * - if a change is already in target in a non executed state, it is probably in a corrupted state. So the origin change is migrated with date=now,
       * so it's the one it will be prioritised over the older ones
       */
      for (ChangeEntry originalChange : changesToMigrate) {
        boolean hasBeenPreviouslyMigrated = allMigratedChanges.contains(String.format("%s-%s", originalChange.getChangeId(), originalChange.getAuthor()));
        boolean migratedAndExecutedState = executedChanges.contains(String.format("%s-%s", originalChange.getChangeId(), originalChange.getAuthor()));
        if (migratedAndExecutedState) {
          logAlreadyTracked(originalChange);
        } else {
          final ChangeEntry changeToInsert;
          if (hasBeenPreviouslyMigrated) {
            changeToInsert = new ChangeEntry(
                originalChange.getExecutionId(),
                originalChange.getChangeId(),
                originalChange.getAuthor(),
                new Date(),
                originalChange.getState(),
                originalChange.getType(),
                originalChange.getChangeLogClass(),
                originalChange.getChangeSetMethod(),
                originalChange.getExecutionMillis(),
                originalChange.getExecutionHostname(),
                originalChange.getMetadata(),
                originalChange.getErrorTrace().orElse(""),
                false);
          } else {
            changeToInsert = originalChange;
          }
          logTracking(changeToInsert);
          changeEntryService.saveOrUpdate(changeToInsert);
          logSuccessfullyTracked(changeToInsert);
        }
        changesMigrated++;
      }
      if (changesCountExpectation != null && changesCountExpectation != changesMigrated) {
        throw new MongockException(String.format("[legacy-migration] - Expectation [%d] changes migrated. Actual [%d] migrated", changesCountExpectation, changesMigrated));
      }
    } catch (MongockException ex) {
      processException(legacyMigration.isFailFast(), ex);
    } catch (Exception ex) {
      processException(legacyMigration.isFailFast(), new MongockException(ex));
    }

  }

  private void processException(boolean isFailFast, MongockException ex) {
    if (isFailFast) {
      throw new MongockException(ex);
    }
    logger.warn(ex.getMessage());
  }
  private List getOriginalMigrationAsChangeEntryList(MongoCollection originalCollection, LegacyMigration legacyMigration) {

    List originalMigrations = new ArrayList<>();
    LegacyMigrationMappingFields mappingFields = legacyMigration.getMappingFields();

    FindIterable docs = originalCollection.find();
    Iterator iteratorOriginalMigration = docs.iterator();
    String executionId = getExecutionId();
    while (iteratorOriginalMigration.hasNext()) {
      Document changeDocument = iteratorOriginalMigration.next();
      ChangeEntry change = new ChangeEntry(
          executionId,
          getDocumentStringValue(changeDocument, mappingFields.getChangeId()),
          getDocumentStringValue(changeDocument, mappingFields.getAuthor()),
          getDocumentDateValue(changeDocument, mappingFields.getTimestamp()),
          ChangeState.EXECUTED,
          ChangeType.EXECUTION,
          getDocumentStringValue(changeDocument, mappingFields.getChangeLogClass()),
          getDocumentStringValue(changeDocument, mappingFields.getChangeSetMethod()),
          -1L,
          "unknown",
          buildMetadata(changeDocument, mappingFields.getMetadata()),
          false
      );
      originalMigrations.add(change);
    }
    return originalMigrations;
  }

  private Object buildMetadata(Document changeDocument, String field) {
    Map newMetadata = new HashMap<>();
    newMetadata.put("migration-type", "legacy");
    Object originalMetadata;
    if ((originalMetadata = field != null ? changeDocument.getString(field) : null) != null) {
      newMetadata.put("original-metadata", originalMetadata);
    }
    return newMetadata;
  }

  private String getDocumentStringValue(Document changeDocument, String field) {
    return field != null ? changeDocument.getString(field) : null;
  }

  private Date getDocumentDateValue(Document changeDocument, String field) {
    return field != null ? DateUtils.toDate(changeDocument.get(field)) : null;
  }

  private String getExecutionId() {
    return String.format("%s-%s-%d", "legacy_migration", LocalDateTime.now(), new Random().nextInt(999));
  }

  private void validateLegacyMigration(LegacyMigration legacyMigration) {
    if (legacyMigration == null
        || isEmpty(legacyMigration.getOrigin())
        || legacyMigration.getMappingFields() == null
        || isEmpty(legacyMigration.getMappingFields().getChangeId())
        || isEmpty(legacyMigration.getMappingFields().getAuthor())) {
      throw new MongockException("[legacy-migration] - wrong configured. Either is null, or doesn't contain collectionName or mapping fields are wrong");
    }
  }

  private static boolean isEmpty(String text) {
    return text == null || text.isEmpty();
  }

  private void logAlreadyTracked(ChangeEntry originalChange) {
    logger.debug("[legacy-migration] - Change[changeId: {} ][author: {} ] already tracked in Mongock changeLog collection", originalChange.getChangeId(), originalChange.getAuthor());
  }

  private void logSuccessfullyTracked(ChangeEntry originalChange) {
    logger.debug("[legacy-migration] - Change[changeId: {} ][author: {} ] tracked successfully", originalChange.getChangeId(), originalChange.getAuthor());
  }

  private void logTracking(ChangeEntry originalChange) {
    logger.debug("[legacy-migration] - Tracking change[changeId: {} ][author: {} ]...", originalChange.getChangeId(), originalChange.getAuthor());
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy