From c1562450dc0015abcf20981d8587cf20d785ab4d Mon Sep 17 00:00:00 2001 From: Michael Simons Date: Tue, 27 Jul 2021 18:03:48 +0200 Subject: [PATCH] GH-237 - Add `validateOnMigrate` to skip validation of migration-checksums. This closes #237. --- .../neo4j/migrations/cli/MigrationsCli.java | 8 +++++ .../neo4j/migrations/core/ChainBuilder.java | 2 +- .../neo4j/migrations/core/Defaults.java | 7 ++++ .../migrations/core/MigrationsConfig.java | 24 +++++++++++++ .../migrations/core/MigrationsConfigTest.java | 12 +++++++ .../neo4j/migrations/core/MigrationsTest.java | 36 +++++++++++++++++++ .../neo4j/migrations/core/TestBase.java | 1 - .../MigrationsAutoConfiguration.java | 1 + .../autoconfigure/MigrationsProperties.java | 13 +++++++ .../MigrationsAutoConfigurationTest.java | 27 ++++++++++++-- 10 files changed, 126 insertions(+), 5 deletions(-) diff --git a/neo4j-migrations-cli/src/main/java/ac/simons/neo4j/migrations/cli/MigrationsCli.java b/neo4j-migrations-cli/src/main/java/ac/simons/neo4j/migrations/cli/MigrationsCli.java index 0819969a7f..a4104f3b6b 100644 --- a/neo4j-migrations-cli/src/main/java/ac/simons/neo4j/migrations/cli/MigrationsCli.java +++ b/neo4j-migrations-cli/src/main/java/ac/simons/neo4j/migrations/cli/MigrationsCli.java @@ -131,6 +131,13 @@ public static void main(String... args) { ) private boolean verbose; + @Option( + names = { "--validate-on-migrate" }, + description = "Validating helps you verify that the migrations applied to the database match the ones available locally and is on by default.", + defaultValue = Defaults.VALIDATE_ON_MIGRATE_VALUE + ) + private boolean validateOnMigrate; + @Spec private CommandSpec commandSpec; @@ -148,6 +155,7 @@ MigrationsConfig getConfig() { .withPackagesToScan(packagesToScan) .withTransactionMode(transactionMode) .withDatabase(database) + .withValidateOnMigrate(validateOnMigrate) .build(); if (!config.hasPlacesToLookForMigrations()) { diff --git a/neo4j-migrations-core/src/main/java/ac/simons/neo4j/migrations/core/ChainBuilder.java b/neo4j-migrations-core/src/main/java/ac/simons/neo4j/migrations/core/ChainBuilder.java index 40fb207274..0ff5396594 100644 --- a/neo4j-migrations-core/src/main/java/ac/simons/neo4j/migrations/core/ChainBuilder.java +++ b/neo4j-migrations-core/src/main/java/ac/simons/neo4j/migrations/core/ChainBuilder.java @@ -106,7 +106,7 @@ private Map buildChain0(MigrationContext context, Lis "Unexpected migration at index " + i + ": " + Migrations.toString(newMigration)); } - if (!expectedChecksum.equals(newMigration.getChecksum())) { + if (context.getConfig().isValidateOnMigrate() && !expectedChecksum.equals(newMigration.getChecksum())) { throw new MigrationsException(("Checksum of " + Migrations.toString(newMigration) + " changed!")); } // This is not a pending migration anymore diff --git a/neo4j-migrations-core/src/main/java/ac/simons/neo4j/migrations/core/Defaults.java b/neo4j-migrations-core/src/main/java/ac/simons/neo4j/migrations/core/Defaults.java index 9172e88f2f..fcc0824683 100644 --- a/neo4j-migrations-core/src/main/java/ac/simons/neo4j/migrations/core/Defaults.java +++ b/neo4j-migrations-core/src/main/java/ac/simons/neo4j/migrations/core/Defaults.java @@ -62,6 +62,13 @@ public final class Defaults { */ public static final TransactionMode TRANSACTION_MODE = TransactionMode.PER_MIGRATION; + /** + * Default setting for {@code validateOnMigrate}. + */ + public static final boolean VALIDATE_ON_MIGRATE = true; + + public static final String VALIDATE_ON_MIGRATE_VALUE = "true"; + private Defaults() { } } diff --git a/neo4j-migrations-core/src/main/java/ac/simons/neo4j/migrations/core/MigrationsConfig.java b/neo4j-migrations-core/src/main/java/ac/simons/neo4j/migrations/core/MigrationsConfig.java index 0e1f24f3e1..00794c43a7 100644 --- a/neo4j-migrations-core/src/main/java/ac/simons/neo4j/migrations/core/MigrationsConfig.java +++ b/neo4j-migrations-core/src/main/java/ac/simons/neo4j/migrations/core/MigrationsConfig.java @@ -75,6 +75,8 @@ public static MigrationsConfig defaultConfig() { private final String installedBy; + private final boolean validateOnMigrate; + private MigrationsConfig(Builder builder) { this.packagesToScan = builder.packagesToScan == null ? Defaults.PACKAGES_TO_SCAN : builder.packagesToScan; @@ -83,6 +85,7 @@ private MigrationsConfig(Builder builder) { this.transactionMode = Optional.ofNullable(builder.transactionMode).orElse(TransactionMode.PER_MIGRATION); this.database = builder.database; this.installedBy = Optional.ofNullable(builder.installedBy).orElse(System.getProperty("user.name")); + this.validateOnMigrate = builder.validateOnMigrate; } public String[] getPackagesToScan() { @@ -109,6 +112,10 @@ public String getInstalledBy() { return installedBy; } + public boolean isValidateOnMigrate() { + return validateOnMigrate; + } + /** * A builder to create new instances of {@link MigrationsConfig configurations}. */ @@ -124,6 +131,8 @@ public static class Builder { private String installedBy; + private boolean validateOnMigrate = Defaults.VALIDATE_ON_MIGRATE; + /** * Configures the list of packages to scan. Default is an empty list. * @@ -188,6 +197,21 @@ public Builder withInstalledBy(String newInstalledBy) { return this; } + /** + * Validating helps you verify that the migrations applied to the database match the ones available locally and + * is on by default. It can be turned off by using a configuration with {@link MigrationsConfig#isValidateOnMigrate()} + * to {@literal false}. + * + * @param newValidateOnMigrate The new value for {@code validateOnMigrate}. + * @return The builder for further customization + * @since 0.1.5 + */ + public Builder withValidateOnMigrate(boolean newValidateOnMigrate) { + + this.validateOnMigrate = newValidateOnMigrate; + return this; + } + /** * @return The immutable configuration */ diff --git a/neo4j-migrations-core/src/test/java/ac/simons/neo4j/migrations/core/MigrationsConfigTest.java b/neo4j-migrations-core/src/test/java/ac/simons/neo4j/migrations/core/MigrationsConfigTest.java index 8b86a4a34d..dd18cce32d 100644 --- a/neo4j-migrations-core/src/test/java/ac/simons/neo4j/migrations/core/MigrationsConfigTest.java +++ b/neo4j-migrations-core/src/test/java/ac/simons/neo4j/migrations/core/MigrationsConfigTest.java @@ -29,4 +29,16 @@ void shouldConfigureDefaultClasspathPackage() { assertThat(MigrationsConfig.builder().build().getLocationsToScan()).isEqualTo(Defaults.LOCATIONS_TO_SCAN); } + + @Test // GH-237 + void validateOnInstallShouldBeTrueByDefault() { + + assertThat(MigrationsConfig.builder().build().isValidateOnMigrate()).isTrue(); + } + + @Test // GH-237 + void validateOnInstallShouldBeChangeable() { + + assertThat(MigrationsConfig.builder().withValidateOnMigrate(false).build().isValidateOnMigrate()).isFalse(); + } } diff --git a/neo4j-migrations-core/src/test/java/ac/simons/neo4j/migrations/core/MigrationsTest.java b/neo4j-migrations-core/src/test/java/ac/simons/neo4j/migrations/core/MigrationsTest.java index c8aa8bc555..51b2c0cef4 100644 --- a/neo4j-migrations-core/src/test/java/ac/simons/neo4j/migrations/core/MigrationsTest.java +++ b/neo4j-migrations-core/src/test/java/ac/simons/neo4j/migrations/core/MigrationsTest.java @@ -120,6 +120,42 @@ void shouldFailWithChangedMigrations() throws IOException { } } + @Test // GH-237 + void changedMigrationsShouldBeAllowedWhenValidateIsOff() throws IOException { + + File dir = Files.createTempDirectory("neo4j-migrations").toFile(); + List files = createMigrationFiles(2, dir); + + try { + String location = "file:" + dir.getAbsolutePath(); + MigrationsConfig configuration = MigrationsConfig.builder().withLocationsToScan(location).withValidateOnMigrate(false).build(); + Migrations migrations = new Migrations(configuration, driver); + migrations.apply(); + + assertThat(lengthOfMigrations(driver, null)).isEqualTo(2); + + Files.write(files.get(1).toPath(), Arrays.asList("MATCH (n) RETURN n;", "CREATE (m:SomeNode) RETURN m;")); + + File newMigration = new File(dir, "V3__SomethingNew.cypher"); + files.add(newMigration); + Files.write(newMigration.toPath(), Arrays.asList("CREATE INDEX node_index_name FOR (n:Person) ON (n.surname)")); + + migrations = new Migrations(configuration, driver); + migrations.apply(); + + try (Session session = driver.session()) { + String version = session.run( + "MATCH (:__Neo4jMigration {version: '2'}) -[r:MIGRATED_TO]->(t) RETURN t.version AS version") + .single().get("version").asString(); + assertThat(version).isEqualTo("3"); + } + } finally { + for (File file : files) { + file.delete(); + } + } + } + @Test void shouldVerifyChecksums() throws IOException { diff --git a/neo4j-migrations-core/src/test/java/ac/simons/neo4j/migrations/core/TestBase.java b/neo4j-migrations-core/src/test/java/ac/simons/neo4j/migrations/core/TestBase.java index 5bb6b74f10..24a472ad38 100644 --- a/neo4j-migrations-core/src/test/java/ac/simons/neo4j/migrations/core/TestBase.java +++ b/neo4j-migrations-core/src/test/java/ac/simons/neo4j/migrations/core/TestBase.java @@ -51,7 +51,6 @@ abstract class TestBase { @BeforeAll void initDriver() { - Config config = Config.builder().withLogging(Logging.none()).build(); neo4j.start(); diff --git a/neo4j-migrations-spring-boot-starter-parent/neo4j-migrations-spring-boot-autoconfigure/src/main/java/ac/simons/neo4j/migrations/springframework/boot/autoconfigure/MigrationsAutoConfiguration.java b/neo4j-migrations-spring-boot-starter-parent/neo4j-migrations-spring-boot-autoconfigure/src/main/java/ac/simons/neo4j/migrations/springframework/boot/autoconfigure/MigrationsAutoConfiguration.java index 2850035ac6..0c02fa2afa 100644 --- a/neo4j-migrations-spring-boot-starter-parent/neo4j-migrations-spring-boot-autoconfigure/src/main/java/ac/simons/neo4j/migrations/springframework/boot/autoconfigure/MigrationsAutoConfiguration.java +++ b/neo4j-migrations-spring-boot-starter-parent/neo4j-migrations-spring-boot-autoconfigure/src/main/java/ac/simons/neo4j/migrations/springframework/boot/autoconfigure/MigrationsAutoConfiguration.java @@ -66,6 +66,7 @@ MigrationsConfig neo4jMigrationsConfig(ResourceLoader resourceLoader, Migrations .withTransactionMode(migrationsProperties.getTransactionMode()) .withDatabase(migrationsProperties.getDatabase()) .withInstalledBy(migrationsProperties.getInstalledBy()) + .withValidateOnMigrate(migrationsProperties.isValidateOnMigrate()) .build(); } diff --git a/neo4j-migrations-spring-boot-starter-parent/neo4j-migrations-spring-boot-autoconfigure/src/main/java/ac/simons/neo4j/migrations/springframework/boot/autoconfigure/MigrationsProperties.java b/neo4j-migrations-spring-boot-starter-parent/neo4j-migrations-spring-boot-autoconfigure/src/main/java/ac/simons/neo4j/migrations/springframework/boot/autoconfigure/MigrationsProperties.java index a51a80f192..e62ab93e4c 100644 --- a/neo4j-migrations-spring-boot-starter-parent/neo4j-migrations-spring-boot-autoconfigure/src/main/java/ac/simons/neo4j/migrations/springframework/boot/autoconfigure/MigrationsProperties.java +++ b/neo4j-migrations-spring-boot-starter-parent/neo4j-migrations-spring-boot-autoconfigure/src/main/java/ac/simons/neo4j/migrations/springframework/boot/autoconfigure/MigrationsProperties.java @@ -71,6 +71,11 @@ public class MigrationsProperties { */ private String installedBy; + /** + * Validating helps you verify that the migrations applied to the database match the ones available locally and is on by default. + */ + private boolean validateOnMigrate = Defaults.VALIDATE_ON_MIGRATE; + public boolean isEnabled() { return enabled; } @@ -134,4 +139,12 @@ public String getDatabase() { public void setDatabase(String database) { this.database = database; } + + public boolean isValidateOnMigrate() { + return validateOnMigrate; + } + + public void setValidateOnMigrate(boolean validateOnMigrate) { + this.validateOnMigrate = validateOnMigrate; + } } diff --git a/neo4j-migrations-spring-boot-starter-parent/neo4j-migrations-spring-boot-autoconfigure/src/test/java/ac/simons/neo4j/migrations/springframework/boot/autoconfigure/MigrationsAutoConfigurationTest.java b/neo4j-migrations-spring-boot-starter-parent/neo4j-migrations-spring-boot-autoconfigure/src/test/java/ac/simons/neo4j/migrations/springframework/boot/autoconfigure/MigrationsAutoConfigurationTest.java index 8d5efeaa64..8eb9c1d901 100644 --- a/neo4j-migrations-spring-boot-starter-parent/neo4j-migrations-spring-boot-autoconfigure/src/test/java/ac/simons/neo4j/migrations/springframework/boot/autoconfigure/MigrationsAutoConfigurationTest.java +++ b/neo4j-migrations-spring-boot-starter-parent/neo4j-migrations-spring-boot-autoconfigure/src/test/java/ac/simons/neo4j/migrations/springframework/boot/autoconfigure/MigrationsAutoConfigurationTest.java @@ -17,6 +17,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assertions.assertThatNoException; import ac.simons.neo4j.migrations.core.Migrations; import ac.simons.neo4j.migrations.core.MigrationsConfig; @@ -151,7 +152,7 @@ void shouldBeHappyWithAtLeastOneLocation() { Mockito.when(resource.exists()).thenReturn(true); MigrationsAutoConfiguration ac = new MigrationsAutoConfiguration(); - ac.neo4jMigrationsConfig(resourceLoader, properties); + assertThatNoException().isThrownBy(() -> ac.neo4jMigrationsConfig(resourceLoader, properties)); } @Test @@ -159,12 +160,32 @@ void shouldBeHappyWithAtLeastOnePackage() { MigrationsProperties properties = new MigrationsProperties(); properties.setPackagesToScan(new String[] { "na" }); - properties.setLocationsToScan(new String[0]); assertThat(properties.isCheckLocation()).isTrue(); MigrationsAutoConfiguration ac = new MigrationsAutoConfiguration(); - ac.neo4jMigrationsConfig(resourceLoader, properties); + assertThatNoException().isThrownBy(() -> ac.neo4jMigrationsConfig(resourceLoader, properties)); + } + + @Test // GH-237 + void validateOnMigrationShouldBeTrueByDefault() { + + MigrationsProperties properties = new MigrationsProperties(); + properties.setPackagesToScan(new String[] { "na" }); + + MigrationsConfig config = new MigrationsAutoConfiguration().neo4jMigrationsConfig(resourceLoader, properties); + assertThat(config.isValidateOnMigrate()).isTrue(); + } + + @Test // GH-237 + void shouldApplyValidateOnMigration() { + + MigrationsProperties properties = new MigrationsProperties(); + properties.setPackagesToScan(new String[] { "na" }); + properties.setValidateOnMigrate(false); + + MigrationsConfig config = new MigrationsAutoConfiguration().neo4jMigrationsConfig(resourceLoader, properties); + assertThat(config.isValidateOnMigrate()).isFalse(); } }