From 3cf24025605903a8942ad69d88aa5eb69588a543 Mon Sep 17 00:00:00 2001 From: filipe Date: Fri, 20 Jan 2023 14:48:55 -0300 Subject: [PATCH] Refactors CalculateChecksumCommandStep (#3696) * Migrates code from Liquibase to TagExists Command step * Documentation + tests --- .../src/main/java/liquibase/Liquibase.java | 60 ++++------- .../core/CalculateChecksumCommandStep.java | 102 ++++++++++++------ .../integration/commandline/Main.java | 4 +- .../command/calculateChecksum.test.groovy | 12 ++- 4 files changed, 103 insertions(+), 75 deletions(-) diff --git a/liquibase-core/src/main/java/liquibase/Liquibase.java b/liquibase-core/src/main/java/liquibase/Liquibase.java index f0be1237493..9d56474585b 100644 --- a/liquibase-core/src/main/java/liquibase/Liquibase.java +++ b/liquibase-core/src/main/java/liquibase/Liquibase.java @@ -7,10 +7,7 @@ import liquibase.changelog.visitor.*; import liquibase.command.CommandResults; import liquibase.command.CommandScope; -import liquibase.command.core.DbUrlConnectionCommandStep; -import liquibase.command.core.InternalDropAllCommandStep; -import liquibase.command.core.TagCommandStep; -import liquibase.command.core.TagExistsCommandStep; +import liquibase.command.core.*; import liquibase.database.Database; import liquibase.database.DatabaseConnection; import liquibase.database.DatabaseFactory; @@ -55,7 +52,10 @@ import liquibase.util.StringUtil; import javax.xml.parsers.ParserConfigurationException; -import java.io.*; +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintStream; +import java.io.Writer; import java.text.DateFormat; import java.util.*; import java.util.function.Supplier; @@ -69,10 +69,6 @@ public class Liquibase implements AutoCloseable { private static final Logger LOG = Scope.getCurrentScope().getLog(Liquibase.class); - protected static final int CHANGESET_ID_NUM_PARTS = 3; - protected static final int CHANGESET_ID_AUTHOR_PART = 2; - protected static final int CHANGESET_ID_CHANGESET_PART = 1; - protected static final int CHANGESET_ID_CHANGELOG_PART = 0; private static final ResourceBundle coreBundle = getBundle("liquibase/i18n/liquibase-core"); public static final String MSG_COULD_NOT_RELEASE_LOCK = coreBundle.getString("could.not.release.lock"); @@ -2202,40 +2198,28 @@ public void run() throws Exception { }); } + /** + * Calculate the checksum for a given identifier + * + * @deprecated Use {link {@link CommandScope(String)}. + */ public final CheckSum calculateCheckSum(final String changeSetIdentifier) throws LiquibaseException { - if (changeSetIdentifier == null) { - throw new LiquibaseException(new IllegalArgumentException("changeSetIdentifier")); - } - final List parts = StringUtil.splitAndTrim(changeSetIdentifier, "::"); - if ((parts == null) || (parts.size() < CHANGESET_ID_NUM_PARTS)) { - throw new LiquibaseException( - new IllegalArgumentException("Invalid changeSet identifier: " + changeSetIdentifier) - ); - } - return this.calculateCheckSum(parts.get(CHANGESET_ID_CHANGELOG_PART), - parts.get(CHANGESET_ID_CHANGESET_PART), parts.get(CHANGESET_ID_AUTHOR_PART)); + CommandResults commandResults = new CommandScope("calculateChecksum") + .addArgumentValue(DbUrlConnectionCommandStep.DATABASE_ARG, database) + .addArgumentValue(CalculateChecksumCommandStep.CHANGESET_IDENTIFIER_ARG, changeSetIdentifier) + .addArgumentValue(CalculateChecksumCommandStep.CHANGELOG_FILE_ARG, this.changeLogFile) + .execute(); + return commandResults.getResult(CalculateChecksumCommandStep.CHECKSUM_RESULT); } + /** + * Calculates the checksum for the values that form a given identifier + * + * @deprecated Use {link {@link CommandScope(String)}. + */ public CheckSum calculateCheckSum(final String filename, final String id, final String author) throws LiquibaseException { - LOG.info(String.format("Calculating checksum for changeset %s::%s::%s", filename, id, author)); - final ChangeLogParameters clParameters = this.getChangeLogParameters(); - final ResourceAccessor resourceAccessor = this.getResourceAccessor(); - final DatabaseChangeLog changeLog = - ChangeLogParserFactory.getInstance().getParser( - this.changeLogFile, resourceAccessor - ).parse(this.changeLogFile, clParameters, resourceAccessor); - - // TODO: validate? - - final ChangeSet changeSet = changeLog.getChangeSet(filename, author, id); - if (changeSet == null) { - throw new LiquibaseException( - new IllegalArgumentException("No such changeSet: " + filename + "::" + id + "::" + author) - ); - } - - return changeSet.generateCheckSum(); + return this.calculateCheckSum(String.format("%s::%s::%s", filename, id, author)); } public void generateDocumentation(String outputDirectory) throws LiquibaseException { diff --git a/liquibase-core/src/main/java/liquibase/command/core/CalculateChecksumCommandStep.java b/liquibase-core/src/main/java/liquibase/command/core/CalculateChecksumCommandStep.java index 46944e234af..c4dfece7a05 100644 --- a/liquibase-core/src/main/java/liquibase/command/core/CalculateChecksumCommandStep.java +++ b/liquibase-core/src/main/java/liquibase/command/core/CalculateChecksumCommandStep.java @@ -1,45 +1,48 @@ package liquibase.command.core; +import liquibase.Scope; +import liquibase.change.CheckSum; +import liquibase.changelog.ChangeLogParameters; +import liquibase.changelog.ChangeSet; +import liquibase.changelog.DatabaseChangeLog; import liquibase.command.*; -import liquibase.configuration.ConfigurationValueObfuscator; -import liquibase.exception.CommandExecutionException; +import liquibase.database.Database; +import liquibase.exception.LiquibaseException; +import liquibase.parser.ChangeLogParserFactory; +import liquibase.resource.ResourceAccessor; +import liquibase.util.StringUtil; -public class CalculateChecksumCommandStep extends AbstractCliWrapperCommandStep { +import java.util.Arrays; +import java.util.List; - public static String[] COMMAND_NAME = {"calculateChecksum"}; +public class CalculateChecksumCommandStep extends AbstractCommandStep { + + protected static final String[] COMMAND_NAME = {"calculateChecksum"}; public static final CommandArgumentDefinition CHANGELOG_FILE_ARG; public static final CommandArgumentDefinition CHANGESET_IDENTIFIER_ARG; - public static final CommandArgumentDefinition URL_ARG; - public static final CommandArgumentDefinition DEFAULT_SCHEMA_NAME_ARG; - public static final CommandArgumentDefinition DEFAULT_CATALOG_NAME_ARG; - public static final CommandArgumentDefinition USERNAME_ARG; - public static final CommandArgumentDefinition PASSWORD_ARG; - public static final CommandArgumentDefinition DRIVER_ARG; - public static final CommandArgumentDefinition DRIVER_PROPERTIES_FILE_ARG; + + public static final CommandResultDefinition CHECKSUM_RESULT; + + protected static final int CHANGESET_ID_NUM_PARTS = 3; + protected static final int CHANGESET_ID_AUTHOR_PART = 2; + protected static final int CHANGESET_ID_CHANGESET_PART = 1; + protected static final int CHANGESET_ID_CHANGELOG_PART = 0; + static { CommandBuilder builder = new CommandBuilder(COMMAND_NAME); CHANGELOG_FILE_ARG = builder.argument(CommonArgumentNames.CHANGELOG_FILE, String.class).required() .description("The root changelog file").build(); - URL_ARG = builder.argument(CommonArgumentNames.URL, String.class).required() - .description("The JDBC database connection URL").build(); - DEFAULT_SCHEMA_NAME_ARG = builder.argument("defaultSchemaName", String.class) - .description("The default schema name to use for the database connection").build(); - DEFAULT_CATALOG_NAME_ARG = builder.argument("defaultCatalogName", String.class) - .description("The default catalog name to use for the database connection").build(); - DRIVER_ARG = builder.argument("driver", String.class) - .description("The JDBC driver class").build(); - DRIVER_PROPERTIES_FILE_ARG = builder.argument("driverPropertiesFile", String.class) - .description("The JDBC driver properties file").build(); - USERNAME_ARG = builder.argument(CommonArgumentNames.USERNAME, String.class) - .description("The database username").build(); - PASSWORD_ARG = builder.argument(CommonArgumentNames.PASSWORD, String.class) - .description("The database password") - .setValueObfuscator(ConfigurationValueObfuscator.STANDARD) - .build(); CHANGESET_IDENTIFIER_ARG = builder.argument("changesetIdentifier", String.class).required() .description("Changeset ID identifier of form filepath::id::author").build(); + + CHECKSUM_RESULT = builder.result("checksumResult", CheckSum.class).description("Calculated checksum").build(); + } + + @Override + public List> requiredDependencies() { + return Arrays.asList(Database.class); } @Override @@ -47,10 +50,49 @@ public String[][] defineCommandNames() { return new String[][] { COMMAND_NAME }; } - @Override - protected String[] collectArguments(CommandScope commandScope) throws CommandExecutionException { - return collectArguments(commandScope, null, CHANGESET_IDENTIFIER_ARG.getName()); + public void run(CommandResultsBuilder resultsBuilder) throws Exception { + CommandScope commandScope = resultsBuilder.getCommandScope(); + final String changeSetIdentifier = commandScope.getArgumentValue(CHANGESET_IDENTIFIER_ARG); + final String changeLogFile = commandScope.getArgumentValue(CHANGELOG_FILE_ARG).replace('\\', '/'); + final Database database = (Database) commandScope.getDependency(Database.class); + + List parts = validateAndExtractParts(changeSetIdentifier, changeLogFile); + Scope.getCurrentScope().getLog(getClass()).info(String.format("Calculating checksum for changeset %s", changeSetIdentifier)); + + ResourceAccessor resourceAccessor = Scope.getCurrentScope().getResourceAccessor(); + DatabaseChangeLog changeLog = ChangeLogParserFactory.getInstance().getParser( + changeLogFile, resourceAccessor).parse(changeLogFile, new ChangeLogParameters(database), resourceAccessor); + + ChangeSet changeSet = changeLog.getChangeSet(parts.get(CHANGESET_ID_CHANGELOG_PART), parts.get(CHANGESET_ID_AUTHOR_PART), + parts.get(CHANGESET_ID_CHANGESET_PART)); + if (changeSet == null) { + throw new LiquibaseException(new IllegalArgumentException("No such changeSet: " + changeSetIdentifier)); + } + sendMessages(resultsBuilder, changeSet.generateCheckSum()); + } + + private static void sendMessages(CommandResultsBuilder resultsBuilder, CheckSum checkSum) { + resultsBuilder.addResult(CHECKSUM_RESULT, checkSum); + Scope.getCurrentScope().getUI().sendMessage(checkSum.toString()); + } + + private List validateAndExtractParts(String changeSetIdentifier, String changeLogFile) throws LiquibaseException { + if (StringUtil.isEmpty(changeSetIdentifier)) { + throw new LiquibaseException(new IllegalArgumentException(CHANGESET_IDENTIFIER_ARG.getName())); + } + + if (StringUtil.isEmpty(changeLogFile)) { + throw new LiquibaseException(new IllegalArgumentException(CHANGELOG_FILE_ARG.getName())); + } + + final List parts = StringUtil.splitAndTrim(changeSetIdentifier, "::"); + if ((parts == null) || (parts.size() < CHANGESET_ID_NUM_PARTS)) { + throw new LiquibaseException( + new IllegalArgumentException("Invalid changeSet identifier: " + changeSetIdentifier) + ); + } + return parts; } @Override diff --git a/liquibase-core/src/main/java/liquibase/integration/commandline/Main.java b/liquibase-core/src/main/java/liquibase/integration/commandline/Main.java index 60c4d8ed41c..7a4ff356300 100644 --- a/liquibase-core/src/main/java/liquibase/integration/commandline/Main.java +++ b/liquibase-core/src/main/java/liquibase/integration/commandline/Main.java @@ -1793,9 +1793,7 @@ protected void doMigration() throws Exception { liquibase.clearCheckSums(); return; } else if (COMMANDS.CALCULATE_CHECKSUM.equalsIgnoreCase(command)) { - CheckSum checkSum = null; - checkSum = liquibase.calculateCheckSum(commandParams.iterator().next()); - Scope.getCurrentScope().getUI().sendMessage(checkSum.toString()); + liquibase.calculateCheckSum(commandParams.iterator().next()); return; } else if (COMMANDS.DB_DOC.equalsIgnoreCase(command)) { if (commandParams.isEmpty()) { diff --git a/liquibase-integration-tests/src/test/resources/liquibase/extension/testing/command/calculateChecksum.test.groovy b/liquibase-integration-tests/src/test/resources/liquibase/extension/testing/command/calculateChecksum.test.groovy index 48e682b4e73..17d28a290f8 100644 --- a/liquibase-integration-tests/src/test/resources/liquibase/extension/testing/command/calculateChecksum.test.groovy +++ b/liquibase-integration-tests/src/test/resources/liquibase/extension/testing/command/calculateChecksum.test.groovy @@ -23,10 +23,10 @@ Optional Args: Default: null driverPropertiesFile (String) The JDBC driver properties file Default: null - password (String) The database password + password (String) Password to use to connect to the database Default: null OBFUSCATED - username (String) The database username + username (String) Username to use to connect to the database Default: null """ @@ -40,7 +40,7 @@ Optional Args: ] expectedResults = [ - statusCode : 0 + checksumResult : "8:b6084e5d5f46b534bbbe18a0d35d34e0" ] } @@ -50,6 +50,7 @@ Optional Args: ] expectedException = CommandValidationException.class + expectedExceptionMessage = 'Invalid argument \'changelogFile\': missing required argument' } run "Run without changesetIdentifier should throw an exception", { @@ -58,14 +59,17 @@ Optional Args: ] expectedException = CommandValidationException.class + expectedExceptionMessage = "Invalid argument \'changesetIdentifier\': missing required argument" } run "Run without URL should throw an exception", { arguments = [ url: "", - changelogFile : "changelogs/h2/complete/rollback.tag.changelog.xml" + changelogFile : "changelogs/h2/complete/rollback.tag.changelog.xml", + changesetIdentifier: "changelogs/h2/complete/rollback.tag.changelog.xml::1::nvoxland", ] expectedException = CommandValidationException.class + expectedExceptionMessage = "Invalid argument \'url\': missing required argument" } }