From 4f0af6e80244be7e1fe51d03c9694e36af335846 Mon Sep 17 00:00:00 2001 From: Wesley Willard Date: Fri, 3 Dec 2021 12:42:59 -0600 Subject: [PATCH 01/37] Fix issue with ConfiguredValue.wasDefaultValueUsed method Added more test setup code to create/delete resources DAT-8640 --- .../command/core/UpdateCommandStep.java | 2 +- .../configuration/ConfiguredValue.java | 10 +- .../main/java/liquibase/util/FileUtil.java | 2 +- .../testing/command/CommandTests.groovy | 92 ++++++++++++------- .../testing/setup/SetupCleanResources.groovy | 57 ++++++++---- .../SetupCreateDirectoryResources.groovy | 23 +++++ .../setup/SetupCreateTempResources.groovy | 8 +- .../testing/command/diffChangelog.test.groovy | 7 +- .../command/generateChangelog.test.groovy | 3 +- .../command/registerChangelog.test.groovy | 1 - 10 files changed, 137 insertions(+), 68 deletions(-) create mode 100644 liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/setup/SetupCreateDirectoryResources.groovy diff --git a/liquibase-core/src/main/java/liquibase/command/core/UpdateCommandStep.java b/liquibase-core/src/main/java/liquibase/command/core/UpdateCommandStep.java index 431a6c6622a..9c74e496517 100644 --- a/liquibase-core/src/main/java/liquibase/command/core/UpdateCommandStep.java +++ b/liquibase-core/src/main/java/liquibase/command/core/UpdateCommandStep.java @@ -26,7 +26,7 @@ public class UpdateCommandStep extends AbstractCliWrapperCommandStep { CommandBuilder builder = new CommandBuilder(COMMAND_NAME, LEGACY_COMMAND_NAME); URL_ARG = builder.argument("url", String.class).required() - .description("The JDBC database connection URL").build(); + .description("The JDBC database connection URL").build(); DEFAULT_SCHEMA_NAME = 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) diff --git a/liquibase-core/src/main/java/liquibase/configuration/ConfiguredValue.java b/liquibase-core/src/main/java/liquibase/configuration/ConfiguredValue.java index 23e8c57c444..483175ae691 100644 --- a/liquibase-core/src/main/java/liquibase/configuration/ConfiguredValue.java +++ b/liquibase-core/src/main/java/liquibase/configuration/ConfiguredValue.java @@ -56,15 +56,9 @@ public ProvidedValue getProvidedValue() { return getProvidedValues().get(0); } - public boolean wasDefaultValueUsed() { - for (ProvidedValue providedValue : this.getProvidedValues()) { - if (providedValue.getProvider() != null && providedValue.getProvider() instanceof ConfigurationDefinition.DefaultValueProvider) { - return true; - } - } - - return false; + ProvidedValue winningProvidedValue = getProvidedValue(); + return winningProvidedValue != null && winningProvidedValue.getProvider() instanceof ConfigurationDefinition.DefaultValueProvider; } /** diff --git a/liquibase-core/src/main/java/liquibase/util/FileUtil.java b/liquibase-core/src/main/java/liquibase/util/FileUtil.java index 699cb75bf31..92da8993f4b 100644 --- a/liquibase-core/src/main/java/liquibase/util/FileUtil.java +++ b/liquibase-core/src/main/java/liquibase/util/FileUtil.java @@ -11,7 +11,7 @@ private FileUtil() { throw new IllegalStateException("This utility class must not be instantiated. Sorry."); } - public static String getContents(File file) throws IOException { + public static String getContents(File file) throws IOException { if (!file.exists()) { return null; } diff --git a/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/command/CommandTests.groovy b/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/command/CommandTests.groovy index abcc9b8db99..b61aad4ffdc 100644 --- a/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/command/CommandTests.groovy +++ b/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/command/CommandTests.groovy @@ -21,6 +21,7 @@ import liquibase.database.jvm.JdbcConnection import liquibase.extension.testing.TestDatabaseConnections import liquibase.extension.testing.TestFilter import liquibase.extension.testing.setup.* +import liquibase.extension.testing.setup.SetupCleanResources.CleanupMode import liquibase.hub.HubService import liquibase.hub.core.MockHubService import liquibase.integration.commandline.LiquibaseCommandLineConfiguration @@ -290,13 +291,6 @@ Long Description: ${commandDefinition.getLongDescription() ?: "NOT SET"} return } } - finally { - if (testDef.setup != null) { - for (def setup : testDef.setup) { - setup.cleanup() - } - } - } } as Scope.ScopedRunnerWithReturn) if (savedException != null && savedException.getCause() != null && savedException.getCause() instanceof CommandFailedException) { @@ -311,35 +305,43 @@ Long Description: ${commandDefinition.getLongDescription() ?: "NOT SET"} throw new RuntimeException("Results were expected but none were found for " + testDef.commandTestDefinition.command) } - then: - checkOutput("Command Output", outputStream.toString(), testDef.expectedOutput) - checkOutput("UI Output", uiOutputWriter.toString(), testDef.expectedUI) - checkOutput("UI Error Output", uiErrorWriter.toString(), testDef.expectedUIErrors) - checkOutput("Log Messages", logService.getLogAsString(Level.FINE), testDef.expectedLogs) - - checkFileContent(testDef.expectedFileContent, "Command File Content") - checkDatabaseContent(testDef.expectedDatabaseContent, database, "Database snapshot content") - - if (!testDef.expectedResults.isEmpty()) { - for (def returnedResult : results.getResults().entrySet()) { - def expectedResult = testDef.expectedResults.get(returnedResult.getKey()) - def expectedValue = expectedResult instanceof Closure ? expectedResult.call() : String.valueOf(expectedResult) - def seenValue = String.valueOf(returnedResult.getValue()) - - assert expectedValue != "null": "No expectedResult for returned result '" + returnedResult.getKey() + "' of: " + seenValue - assert seenValue == expectedValue + try { + checkOutput("Command Output", outputStream.toString(), testDef.expectedOutput) + checkOutput("UI Output", uiOutputWriter.toString(), testDef.expectedUI) + checkOutput("UI Error Output", uiErrorWriter.toString(), testDef.expectedUIErrors) + checkOutput("Log Messages", logService.getLogAsString(Level.FINE), testDef.expectedLogs) + + checkFileContent(testDef.expectedFileContent, "Command File Content") + checkDatabaseContent(testDef.expectedDatabaseContent, database, "Database snapshot content") + + if (!testDef.expectedResults.isEmpty()) { + for (def returnedResult : results.getResults().entrySet()) { + def expectedResult = testDef.expectedResults.get(returnedResult.getKey()) + def expectedValue = expectedResult instanceof Closure ? expectedResult.call() : String.valueOf(expectedResult) + def seenValue = String.valueOf(returnedResult.getValue()) + + assert expectedValue != "null": "No expectedResult for returned result '" + returnedResult.getKey() + "' of: " + seenValue + assert seenValue == expectedValue + } + } + if (testDef.expectFileToExist != null) { + assert testDef.expectFileToExist.exists(): "File '${testDef.expectFileToExist.getAbsolutePath()}' should exist" + } + if (testDef.expectFileToNotExist != null) { + assert !testDef.expectFileToNotExist.exists(): "File '${testDef.expectFileToNotExist.getAbsolutePath()}' should not exist" + } + } finally { + if (testDef.setup != null) { + for (def setup : testDef.setup) { + setup.cleanup() + } + } } - } - if (testDef.expectFileToExist != null) { - assert testDef.expectFileToExist.exists(): "File '${testDef.expectFileToExist.getAbsolutePath()}' should exist" - } - if (testDef.expectFileToNotExist != null) { - assert !testDef.expectFileToNotExist.exists(): "File '${testDef.expectFileToNotExist.getAbsolutePath()}' should not exist" - } - where: - permutation << getAllRunTestPermutations() + + where: + permutation << getAllRunTestPermutations() } static OutputCheck assertNotContains(String substring) { @@ -836,10 +838,21 @@ Long Description: ${commandDefinition.getLongDescription() ?: "NOT SET"} this.setups.add(new SetupRunChangelog(changeLogPath, labels)) } + /* + * Create files and directories + */ void createTempResource(String originalFile, String newFile) { this.setups.add(new SetupCreateTempResources(originalFile, newFile)) } + void createTempResource(String originalFile, String newFile, String baseDir) { + this.setups.add(new SetupCreateTempResources(originalFile, newFile, baseDir)) + } + + void createTempDirectoryResource(String directory) { + this.setups.add(new SetupCreateDirectoryResources(directory)) + } + /** * * Copy a specified file to another path @@ -865,13 +878,24 @@ Long Description: ${commandDefinition.getLongDescription() ?: "NOT SET"} * * Delete the specified resources * - * @param fileToDeletes + * @param filesToDelete * */ void cleanResources(String... filesToDelete) { this.setups.add(new SetupCleanResources(filesToDelete)) } + /** + * + * Delete the specified resources at possibly setup and cleanup + * + * @param filesToDelete + * + */ + void cleanResources(CleanupMode cleanOnSetup, String... filesToDelete) { + this.setups.add(new SetupCleanResources(cleanOnSetup, filesToDelete)) + } + /** * Mark the changeSets within a changelog as ran without actually running them */ diff --git a/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/setup/SetupCleanResources.groovy b/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/setup/SetupCleanResources.groovy index 66cb594cf82..40a8b8b60f2 100644 --- a/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/setup/SetupCleanResources.groovy +++ b/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/setup/SetupCleanResources.groovy @@ -1,41 +1,62 @@ package liquibase.extension.testing.setup -import liquibase.Contexts -import liquibase.LabelExpression -import liquibase.Liquibase -import liquibase.changelog.ChangeLogHistoryService -import liquibase.changelog.ChangeLogHistoryServiceFactory -import liquibase.database.Database -import liquibase.database.DatabaseFactory -import liquibase.database.jvm.JdbcConnection + import liquibase.extension.testing.TestDatabaseConnections -import liquibase.integration.commandline.CommandLineResourceAccessor -import liquibase.resource.CompositeResourceAccessor -import liquibase.resource.FileSystemResourceAccessor -import java.nio.file.Paths +import java.nio.file.FileSystems +import java.nio.file.Files +import java.nio.file.Path class SetupCleanResources extends TestSetup { private final List resourcesToDelete = new ArrayList<>() + public enum CleanupMode { CLEAN_ON_SETUP, CLEAN_ON_CLEANUP, CLEAN_ON_BOTH} + private CleanupMode cleanupMode SetupCleanResources(String[] resourcesToDelete) { + this(CleanupMode.CLEAN_ON_CLEANUP, resourcesToDelete) + } + + SetupCleanResources(CleanupMode cleanupMode, String[] resourcesToDelete) { + this.cleanupMode = cleanupMode this.resourcesToDelete.addAll(resourcesToDelete as Set) } @Override void setup(TestDatabaseConnections.ConnectionStatus connectionStatus) throws Exception { + if (cleanupMode == CleanupMode.CLEAN_ON_CLEANUP) { + return + } + deleteFiles(resourcesToDelete) + } + + @Override + void cleanup() { + if (cleanupMode == CleanupMode.CLEAN_ON_SETUP) { + return + } + deleteFiles(resourcesToDelete) + } + + private void deleteFiles(List resourcesToDelete) { for (String fileToDelete : resourcesToDelete) { + File f = null URL url = Thread.currentThread().getContextClassLoader().getResource(fileToDelete) if (url == null) { - return + f = new File(fileToDelete) + } else { + f = new File(url.toURI()) } - File f = new File(url.toURI()) + + // + // This will handle files and directories + // if (f.exists()) { - boolean b = f.delete() - if (b) { - assert !f.exists(): "The file '$f' was not deleted" - } + Path path = FileSystems.getDefault().getPath(f.getAbsolutePath()); + Files.walk(path) + .sorted(Comparator.reverseOrder()) + .map({ p -> p.toFile() }) + .forEach({ file -> file.delete() }) } } } diff --git a/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/setup/SetupCreateDirectoryResources.groovy b/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/setup/SetupCreateDirectoryResources.groovy new file mode 100644 index 00000000000..4bdb2201240 --- /dev/null +++ b/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/setup/SetupCreateDirectoryResources.groovy @@ -0,0 +1,23 @@ +package liquibase.extension.testing.setup + +import liquibase.extension.testing.TestDatabaseConnections + +class SetupCreateDirectoryResources extends TestSetup { + + private String directory + + SetupCreateDirectoryResources(String directory) { + this.directory = directory + } + + @Override + void setup(TestDatabaseConnections.ConnectionStatus connectionStatus) throws Exception { + File f = new File(directory) + boolean b = f.mkdirs() + if (! b) { + if (! f.exists()) { + throw new RuntimeException("Unable to create directory '" + directory + "'") + } + } + } +} diff --git a/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/setup/SetupCreateTempResources.groovy b/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/setup/SetupCreateTempResources.groovy index cb015f02577..0de15a1aebd 100644 --- a/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/setup/SetupCreateTempResources.groovy +++ b/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/setup/SetupCreateTempResources.groovy @@ -7,10 +7,16 @@ class SetupCreateTempResources extends TestSetup { private String originalFile private String newFile + private String baseDir SetupCreateTempResources(String originalFile, String newFile) { + this(originalFile, newFile, "target/test-classes") + } + + SetupCreateTempResources(String originalFile, String newFile, String baseDir) { this.originalFile = originalFile this.newFile = newFile + this.baseDir = baseDir } @Override @@ -18,7 +24,7 @@ class SetupCreateTempResources extends TestSetup { URL url = Thread.currentThread().getContextClassLoader().getResource(originalFile) File f = new File(url.toURI()) String contents = FileUtil.getContents(f) - File outputFile = new File("target/test-classes", newFile) + File outputFile = new File(baseDir, newFile) FileUtil.write(contents, outputFile) } } diff --git a/liquibase-integration-tests/src/test/resources/liquibase/extension/testing/command/diffChangelog.test.groovy b/liquibase-integration-tests/src/test/resources/liquibase/extension/testing/command/diffChangelog.test.groovy index 1400e4eb921..d802cb4d082 100644 --- a/liquibase-integration-tests/src/test/resources/liquibase/extension/testing/command/diffChangelog.test.groovy +++ b/liquibase-integration-tests/src/test/resources/liquibase/extension/testing/command/diffChangelog.test.groovy @@ -7,6 +7,7 @@ import liquibase.change.core.AddPrimaryKeyChange import liquibase.change.core.CreateTableChange import liquibase.exception.CommandExecutionException import liquibase.exception.CommandValidationException +import liquibase.extension.testing.setup.SetupCleanResources import liquibase.structure.core.Column import java.util.regex.Pattern @@ -71,7 +72,7 @@ Optional Args: ] setup { - cleanResources("diffChangelog-test.xml") + cleanResources(SetupCleanResources.CleanupMode.CLEAN_ON_SETUP, "diffChangelog-test.xml") database = [ new CreateTableChange( tableName: "FirstTable", @@ -115,7 +116,7 @@ Optional Args: ] setup { - cleanResources("diffChangeLog-test.xml") + cleanResources(SetupCleanResources.CleanupMode.CLEAN_ON_SETUP, "diffChangeLog-test.xml") database = [ new CreateTableChange( tableName: "SharedTable", @@ -173,7 +174,7 @@ Optional Args: ] setup { - cleanResources("diffChangelogOrder-test.xml") + cleanResources(SetupCleanResources.CleanupMode.CLEAN_ON_SETUP, "diffChangelogOrder-test.xml") database = [ new CreateTableChange( tableName: "person", diff --git a/liquibase-integration-tests/src/test/resources/liquibase/extension/testing/command/generateChangelog.test.groovy b/liquibase-integration-tests/src/test/resources/liquibase/extension/testing/command/generateChangelog.test.groovy index eae820c7f38..79d5f189281 100644 --- a/liquibase-integration-tests/src/test/resources/liquibase/extension/testing/command/generateChangelog.test.groovy +++ b/liquibase-integration-tests/src/test/resources/liquibase/extension/testing/command/generateChangelog.test.groovy @@ -4,6 +4,7 @@ import liquibase.change.ColumnConfig import liquibase.change.core.CreateTableChange import liquibase.change.core.TagDatabaseChange import liquibase.exception.CommandValidationException +import liquibase.extension.testing.setup.SetupCleanResources CommandTests.define { command = ["generateChangelog"] @@ -51,7 +52,7 @@ Optional Args: changelogFile: "target/test-classes/changelog-test.xml" ] setup { - cleanResources("changelog-test.xml") + cleanResources(SetupCleanResources.CleanupMode.CLEAN_ON_SETUP, "changelog-test.xml") database = [ new CreateTableChange( tableName: "FirstTable", diff --git a/liquibase-integration-tests/src/test/resources/liquibase/extension/testing/command/registerChangelog.test.groovy b/liquibase-integration-tests/src/test/resources/liquibase/extension/testing/command/registerChangelog.test.groovy index eb83e9da85b..28bdd453618 100644 --- a/liquibase-integration-tests/src/test/resources/liquibase/extension/testing/command/registerChangelog.test.groovy +++ b/liquibase-integration-tests/src/test/resources/liquibase/extension/testing/command/registerChangelog.test.groovy @@ -2,7 +2,6 @@ package liquibase.extension.testing.command import liquibase.exception.CommandExecutionException import liquibase.exception.CommandValidationException -import liquibase.extension.testing.setup.SetupCreateTempResources import liquibase.hub.core.MockHubService import java.util.regex.Pattern From acfc833049d21aae719e6e6f3ca121ab033e7a95 Mon Sep 17 00:00:00 2001 From: Wesley Willard Date: Fri, 3 Dec 2021 17:16:58 -0600 Subject: [PATCH 02/37] Added a comment DAT-8724 --- .../java/liquibase/configuration/ConfiguredValue.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/liquibase-core/src/main/java/liquibase/configuration/ConfiguredValue.java b/liquibase-core/src/main/java/liquibase/configuration/ConfiguredValue.java index 483175ae691..1d0052238ff 100644 --- a/liquibase-core/src/main/java/liquibase/configuration/ConfiguredValue.java +++ b/liquibase-core/src/main/java/liquibase/configuration/ConfiguredValue.java @@ -45,7 +45,6 @@ public DataType getValueObfuscated() { return rawValue; } - /** * Returns the "winning" value across all the possible {@link ConfigurationValueProvider}. * A {@link ProvidedValue} is always returned, even if the value was not configured. @@ -56,6 +55,13 @@ public ProvidedValue getProvidedValue() { return getProvidedValues().get(0); } + /** + * + * Return true if a default value was the "winning" value + * + * @return boolean + * + */ public boolean wasDefaultValueUsed() { ProvidedValue winningProvidedValue = getProvidedValue(); return winningProvidedValue != null && winningProvidedValue.getProvider() instanceof ConfigurationDefinition.DefaultValueProvider; From 463e0b8b7c49cabd5fa9b290cf198497020945ff Mon Sep 17 00:00:00 2001 From: Wesley Willard Date: Tue, 7 Dec 2021 15:43:18 -0600 Subject: [PATCH 03/37] Potential fix for JDK 16 issue in CommandTests --- .../extension/testing/setup/SetupCleanResources.groovy | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/setup/SetupCleanResources.groovy b/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/setup/SetupCleanResources.groovy index 40a8b8b60f2..beddeb51ef7 100644 --- a/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/setup/SetupCleanResources.groovy +++ b/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/setup/SetupCleanResources.groovy @@ -48,9 +48,16 @@ class SetupCleanResources extends TestSetup { f = new File(url.toURI()) } + if (f.isFile()) { + f.delete() + } else { + f.deleteDir() + } + // // This will handle files and directories // + /* if (f.exists()) { Path path = FileSystems.getDefault().getPath(f.getAbsolutePath()); Files.walk(path) @@ -58,6 +65,7 @@ class SetupCleanResources extends TestSetup { .map({ p -> p.toFile() }) .forEach({ file -> file.delete() }) } + */ } } } From 2c70056777f6753a1d685f663da31dbb91eb1c11 Mon Sep 17 00:00:00 2001 From: Steven Massaro Date: Thu, 9 Dec 2021 08:30:01 -0500 Subject: [PATCH 04/37] Move interactive prompting logic into core module (DAT-8642) (#2255) This PR (in combination with the Pro PR) does a few small things: Move the interactive CLI prompting logic into the core modules so they can be used outside of pro Add an interface for the enum which dictates the prompting logic. This is so that we can have separate promptable enums in the core and pro code and use the same abstract prompting logic for both. --- .../AbstractCommandLineValueGetter.java | 136 ++++++++++++++++++ .../ui/interactive/DynamicRuleParameter.java | 65 +++++++++ .../IInteractivelyPromptableEnum.java | 29 ++++ ...ractivePromptableCustomizationWrapper.java | 69 +++++++++ 4 files changed, 299 insertions(+) create mode 100644 liquibase-core/src/main/java/liquibase/ui/interactive/AbstractCommandLineValueGetter.java create mode 100644 liquibase-core/src/main/java/liquibase/ui/interactive/DynamicRuleParameter.java create mode 100644 liquibase-core/src/main/java/liquibase/ui/interactive/IInteractivelyPromptableEnum.java create mode 100644 liquibase-core/src/main/java/liquibase/ui/interactive/InteractivePromptableCustomizationWrapper.java diff --git a/liquibase-core/src/main/java/liquibase/ui/interactive/AbstractCommandLineValueGetter.java b/liquibase-core/src/main/java/liquibase/ui/interactive/AbstractCommandLineValueGetter.java new file mode 100644 index 00000000000..864add7e9b9 --- /dev/null +++ b/liquibase-core/src/main/java/liquibase/ui/interactive/AbstractCommandLineValueGetter.java @@ -0,0 +1,136 @@ +package liquibase.ui.interactive; + +import liquibase.Scope; +import liquibase.ui.InputHandler; + +import java.util.List; + +/** + * This class represents the basis for prompts to the user to input values, and should be used as part of the process + * of obtaining user input for quality checks. + * @param the type the user is expected to enter + */ +public abstract class AbstractCommandLineValueGetter { + + /** + * The type of the value that will be obtained. + */ + private final Class clazz; + + /** + * Create a new value getter. + * @param clazz the type of the value that will be obtained + */ + public AbstractCommandLineValueGetter(Class clazz) { + this.clazz = clazz; + } + + /** + * Prompt the user to enter a value for a parameter, and include the existing value in the prompt. + * @param parameter the parameter to obtain a value for + * @param newParameterValues a list of the values that have already been entered by the user during this interactive CLI parameter prompting situation + * @return the value entered by the user + */ + public final T prompt(InteractivePromptableCustomizationWrapper parameter, List newParameterValues, Object currentValue) { + // determine which value should be displayed in square brackets as the value that will be selected if user presses enter + Object valueToPromptAsDefault; + if (currentValue != null) { + /* + * The value is being converted here, because enum types get read from the check settings file as strings. + * As a result, the default value and current value (which should be the same type), are not; the default + * value would be an enum and the current value would be a string. + */ + try { + valueToPromptAsDefault = convert((String) currentValue); + } catch (Exception e) { + valueToPromptAsDefault = currentValue; + } + } else { + valueToPromptAsDefault = parameter.getDefaultValue(); + } + + // assume the user just hit enter to accept the default and revalidate it, since hitting enter to accept the + // default skips the validation logic in ConsoleUIService. Reprompt endlessly until a valid value is entered. + T prompt = null; + boolean valid = false; + while (!valid && (prompt == null || prompt.equals(valueToPromptAsDefault))) { + prompt = doPrompt(parameter, newParameterValues, valueToPromptAsDefault, (currentValue != null || parameter.getDefaultValue() != null)); + + try { + valid = doValidate(parameter, newParameterValues, prompt); + } catch (IllegalArgumentException e) { + Scope.getCurrentScope().getUI().sendErrorMessage("Invalid value: '" + prompt + "': " + e.getMessage()); + } + } + return prompt; + } + + private T doPrompt(InteractivePromptableCustomizationWrapper parameter, List newParameterValues, Object valueToPromptAsDefault, boolean shouldAllowEmptyValues) { + return Scope.getCurrentScope().getUI().prompt(getMessage(parameter), (T) valueToPromptAsDefault, new InputHandler() { + @Override + public T parseInput(String input, Class type) throws IllegalArgumentException { + T convert; + try { + convert = AbstractCommandLineValueGetter.this.convert(input); + } catch (Exception e) { + if (e.getMessage() != null) { + throw new IllegalArgumentException( + String.format("Invalid value: '%s': %s", input, e.getMessage()), e); + } + throw new IllegalArgumentException(e); + } + + try { + if (!doValidate(parameter, newParameterValues, convert)) { + throw new IllegalArgumentException("The supplied value is not valid."); + } + } catch (Exception e) { + if (e.getMessage() != null) { + throw new IllegalArgumentException( + String.format("Invalid value: '%s': %s", input, e.getMessage()), e); + } + throw new IllegalArgumentException( + String.format("Invalid value: '%s': The supplied value is not valid.", input), e); + } + return convert; + } + + @Override + public boolean shouldAllowEmptyInput() { + // We do not allow empty input, because as of right now, all parameters require values. In the future, + // there may be parameters which permit empty values, which would need to tie in here. + return shouldAllowEmptyValues; + } + }, clazz); + } + + private boolean doValidate(InteractivePromptableCustomizationWrapper parameter, List newParameterValues, T convert) throws IllegalArgumentException { + if (parameter.getValidationCallbackOverride() != null) { + return parameter.getValidationCallbackOverride().apply(convert, newParameterValues); + } else { + return AbstractCommandLineValueGetter.this.validate(convert); + } + } + + /** + * Generate the prompt message. + * @param parameter the parameter to prompt for + * @return the message + */ + private String getMessage(InteractivePromptableCustomizationWrapper parameter) { + return parameter.getParameter().getUiMessage() + " (options: " + parameter.getParameter().getOptions() + ")"; + } + + /** + * Given the input from the user, after being converted, validate it. + * @return true if it is valid + */ + public abstract boolean validate(T input); + + /** + * Given the raw input string from the user, convert it to the right type. + * @param input the raw input string from the prompt + * @return the converted input + */ + public abstract T convert(String input); +} diff --git a/liquibase-core/src/main/java/liquibase/ui/interactive/DynamicRuleParameter.java b/liquibase-core/src/main/java/liquibase/ui/interactive/DynamicRuleParameter.java new file mode 100644 index 00000000000..16023baf104 --- /dev/null +++ b/liquibase-core/src/main/java/liquibase/ui/interactive/DynamicRuleParameter.java @@ -0,0 +1,65 @@ +package liquibase.ui.interactive; + + +import java.util.Objects; + + +public class DynamicRuleParameter { + + /** + * The actual underlying DynamicRuleParameterEnum that corresponds to this value. We store the parameter as a string, + * because different versions of Liquibase might not contain the enum that the rule requires. We want to be able to + * parse newer conf files with older versions of Liquibase, without a SnakeYaml error that it cannot find the enum + * value. + */ + private String parameter; + private Object value; + + /** + * Constructor for SnakeYaml + */ + public DynamicRuleParameter() { + } + + public DynamicRuleParameter(IInteractivelyPromptableEnum parameter, Object value) { + Objects.requireNonNull(parameter); + this.parameter = parameter.toString(); + this.value = value; + } + + public DynamicRuleParameter(String parameter, Object value) { + Objects.requireNonNull(parameter); + this.parameter = parameter; + this.value = value; + } + + public String getParameter() { + return parameter; + } + + public void setParameter(String parameter) { + this.parameter = parameter; + } + + public Object getValue() { + return value; + } + + public void setValue(Object value) { + this.value = value; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + DynamicRuleParameter that = (DynamicRuleParameter) o; + return Objects.equals(parameter, that.parameter) && value.equals(that.value); + } + + @Override + public int hashCode() { + return Objects.hash(parameter, value); + } +} + diff --git a/liquibase-core/src/main/java/liquibase/ui/interactive/IInteractivelyPromptableEnum.java b/liquibase-core/src/main/java/liquibase/ui/interactive/IInteractivelyPromptableEnum.java new file mode 100644 index 00000000000..ed76c26679b --- /dev/null +++ b/liquibase-core/src/main/java/liquibase/ui/interactive/IInteractivelyPromptableEnum.java @@ -0,0 +1,29 @@ +package liquibase.ui.interactive; + + +public interface IInteractivelyPromptableEnum { + /** + * Description of the parameter to be used in the UI output. + */ + String getDescription(); + + /** + * The default value of the parameter if the parameter has not been customized. + */ + Object getDefaultValue(); + + /** + * The implementation of the value getter that will be used to prompt the user to input a value. + */ + AbstractCommandLineValueGetter getInteractiveCommandLineValueGetter(); + + /** + * Parameter message to be used in the UI output. + */ + String getUiMessage(); + + /* + * Possible options for this parameter + */ + String getOptions(); +} diff --git a/liquibase-core/src/main/java/liquibase/ui/interactive/InteractivePromptableCustomizationWrapper.java b/liquibase-core/src/main/java/liquibase/ui/interactive/InteractivePromptableCustomizationWrapper.java new file mode 100644 index 00000000000..be0659ae562 --- /dev/null +++ b/liquibase-core/src/main/java/liquibase/ui/interactive/InteractivePromptableCustomizationWrapper.java @@ -0,0 +1,69 @@ +package liquibase.ui.interactive; + + +import java.util.List; +import java.util.function.BiFunction; +import java.util.function.Function; + +/** + * This class exists to wrap the parameter enum so that individual usages can provide specific + * overrides of the logic defined in the enum class. You should access the underlying parameter as little as possible + * and instead, access it using getter wrapper methods in this class. That will allow future developers to specify + * custom overrides for each rule (for example, overriding the default value for a specific usage without + * affecting all others). + */ +public class InteractivePromptableCustomizationWrapper { + private final IInteractivelyPromptableEnum parameter; + private final BiFunction, Boolean> validationCallbackOverride; + private final Function, Boolean> shouldPrompt; + + public InteractivePromptableCustomizationWrapper(IInteractivelyPromptableEnum parameter) { + this.parameter = parameter; + this.validationCallbackOverride = null; + this.shouldPrompt = null; + } + + public InteractivePromptableCustomizationWrapper(IInteractivelyPromptableEnum parameter, BiFunction, Boolean> validationCallbackOverride, Function, Boolean> shouldPrompt) { + this.parameter = parameter; + this.validationCallbackOverride = validationCallbackOverride; + this.shouldPrompt = shouldPrompt; + } + + public Object getDefaultValue() { + return parameter.getDefaultValue(); + } + + public IInteractivelyPromptableEnum getParameter() { + return parameter; + } + + public AbstractCommandLineValueGetter getInteractiveCommandLineValueGetter() { + return parameter.getInteractiveCommandLineValueGetter(); + } + + public BiFunction, Boolean> getValidationCallbackOverride() { + return validationCallbackOverride; + } + + public Function, Boolean> getShouldPrompt() { + return shouldPrompt; + } + + @Override + public String toString() { + return getParameter().toString(); + } + + /** + * Determine if the rule parameter should be prompted for in a checks customize scenario. + * @param existingNewValues the values already provided in the checks customize session + * @return true if the prompt should occur for this parameter, false if not + */ + public boolean shouldPrompt(List existingNewValues) { + boolean resp = true; + if (shouldPrompt != null) { + resp = shouldPrompt.apply(existingNewValues); + } + return resp; + } +} From f9dd0ad53e4ba7e978767e50cb58aa58c5fc0027 Mon Sep 17 00:00:00 2001 From: Wesley Willard Date: Thu, 9 Dec 2021 14:23:35 -0600 Subject: [PATCH 05/37] LB-2177 New content --- .../examples/json/example-changelog.json | 120 ++++++++++++++++++ .../examples/json/example-changeset.json | 30 +++++ .../examples/json/liquibase.properties | 76 +++++++++++ .../liquibase/examples/sql/blankchangelog.sql | 12 ++ .../examples/sql/example-changelog.sql | 23 ++++ .../examples/sql/example-changeset.sql | 7 + .../examples/sql/liquibase.properties | 76 +++++++++++ .../examples/sql/liquibase.sqlplus.conf | 47 +++++++ .../resources/liquibase/examples/start-h2 | 41 ++++++ .../resources/liquibase/examples/start-h2.bat | 34 +++++ .../examples/xml/blank.changelog.xml | 18 +++ .../examples/xml/example-changelog.xml | 43 +++++++ .../examples/xml/example-changeset.xml | 13 ++ .../examples/xml/liquibase.properties | 76 +++++++++++ .../examples/xml/liquibase.sqlplus.conf | 47 +++++++ .../examples/yaml/example-changelog.yaml | 48 +++++++ .../examples/yaml/example-changeset.yaml | 17 +++ .../examples/yaml/liquibase.properties | 76 +++++++++++ .../src/main/assembly/assembly-bin.xml | 7 + .../src/main/install4j/liquibase.install4j | 2 +- 20 files changed, 812 insertions(+), 1 deletion(-) create mode 100644 liquibase-core/src/main/resources/liquibase/examples/json/example-changelog.json create mode 100644 liquibase-core/src/main/resources/liquibase/examples/json/example-changeset.json create mode 100644 liquibase-core/src/main/resources/liquibase/examples/json/liquibase.properties create mode 100644 liquibase-core/src/main/resources/liquibase/examples/sql/blankchangelog.sql create mode 100644 liquibase-core/src/main/resources/liquibase/examples/sql/example-changelog.sql create mode 100644 liquibase-core/src/main/resources/liquibase/examples/sql/example-changeset.sql create mode 100644 liquibase-core/src/main/resources/liquibase/examples/sql/liquibase.properties create mode 100644 liquibase-core/src/main/resources/liquibase/examples/sql/liquibase.sqlplus.conf create mode 100644 liquibase-core/src/main/resources/liquibase/examples/start-h2 create mode 100644 liquibase-core/src/main/resources/liquibase/examples/start-h2.bat create mode 100644 liquibase-core/src/main/resources/liquibase/examples/xml/blank.changelog.xml create mode 100644 liquibase-core/src/main/resources/liquibase/examples/xml/example-changelog.xml create mode 100644 liquibase-core/src/main/resources/liquibase/examples/xml/example-changeset.xml create mode 100644 liquibase-core/src/main/resources/liquibase/examples/xml/liquibase.properties create mode 100644 liquibase-core/src/main/resources/liquibase/examples/xml/liquibase.sqlplus.conf create mode 100644 liquibase-core/src/main/resources/liquibase/examples/yaml/example-changelog.yaml create mode 100644 liquibase-core/src/main/resources/liquibase/examples/yaml/example-changeset.yaml create mode 100644 liquibase-core/src/main/resources/liquibase/examples/yaml/liquibase.properties diff --git a/liquibase-core/src/main/resources/liquibase/examples/json/example-changelog.json b/liquibase-core/src/main/resources/liquibase/examples/json/example-changelog.json new file mode 100644 index 00000000000..3cef3ef10b4 --- /dev/null +++ b/liquibase-core/src/main/resources/liquibase/examples/json/example-changelog.json @@ -0,0 +1,120 @@ +{ "databaseChangeLog": [ + "changeset": { + "id": "1", + "author": "your.name", + "changes": [ + { + "createTable": + { + "tablename": "person", + "columns": [ + { + "column": { + "name": "id", + "type": "int", + "autoincrement": true, + "constraints": { + "primarykey": true, + "nullable": false + } + } + }, + { + "column": { + "name": "name", + "type": "varchar(50)" + } + }, + { + "column": { + "name": "addresss1", + "type": "varchar(50)" + } + }, + { + "column": { + "name": "addresss2", + "type": "varchar(50)" + } + }, + { + "column": { + "name": "city", + "type": "varchar(30)" + } + } + ] + } + } + ] + }, + "changeset": { + "id": "2", + "author": "your.name", + "changes": [ + { + "createTable": + { + "tablename": "company", + "columns": [ + { + "column": { + "name": "id", + "type": "int", + "autoincrement": true, + "constraints": { + "primarykey": true, + "nullable": false + } + } + }, + { + "column": { + "name": "name", + "type": "varchar(50)" + } + }, + { + "column": { + "name": "addresss1", + "type": "varchar(50)" + } + }, + { + "column": { + "name": "addresss2", + "type": "varchar(50)" + } + }, + { + "column": { + "name": "city", + "type": "varchar(30)" + } + } + ] + } + } + ] + }, + "changeset": { + "id": "3", + "author": "your.name", + "changes": [ + { + "addColumn": + { + "tablename": "company", + "columns": [ + { + "column": { + "name": "country", + "type": "varchar(2)" + } + } + ] + } + } + ] + } +]} diff --git a/liquibase-core/src/main/resources/liquibase/examples/json/example-changeset.json b/liquibase-core/src/main/resources/liquibase/examples/json/example-changeset.json new file mode 100644 index 00000000000..4c7e5d572a7 --- /dev/null +++ b/liquibase-core/src/main/resources/liquibase/examples/json/example-changeset.json @@ -0,0 +1,30 @@ +"changeSet": { + "id": "1", + "author": "dev", + "changes": [ + { + "createTable": { + "tableName": "person", + "columns": [ + { + "column": { + "name": "id", + "type": "int", + "autoIncrement": true, + "constraints": { + "primaryKey": true, + "nullable": false + } + } + }, + { + "column": { + "name": "name", + "type": "varchar(255)" + } + } + ] + } + } + ] +} diff --git a/liquibase-core/src/main/resources/liquibase/examples/json/liquibase.properties b/liquibase-core/src/main/resources/liquibase/examples/json/liquibase.properties new file mode 100644 index 00000000000..5c2fd4e9888 --- /dev/null +++ b/liquibase-core/src/main/resources/liquibase/examples/json/liquibase.properties @@ -0,0 +1,76 @@ +#### _ _ _ _ +## | | (_) (_) | +## | | _ __ _ _ _ _| |__ __ _ ___ ___ +## | | | |/ _` | | | | | '_ \ / _` / __|/ _ \ +## | |___| | (_| | |_| | | |_) | (_| \__ \ __/ +## \_____/_|\__, |\__,_|_|_.__/ \__,_|___/\___| +## | | +## |_| +## +## The liquibase.properties file stores properties which do not change often, +## such as database connection information. Properties stored here save time +## and reduce risk of mistyped command line arguments. +## Learn more: https://www.liquibase.org/documentation/config_properties.html +#### +#### +## Note about relative and absolute paths: +## The liquibase.properties file requires paths for some properties. +## The classpath is the path/to/resources (ex. src/main/resources). +## The changeLogFile path is relative to the classpath. +## The url H2 example below is relative to 'pwd' resource. +#### +# Enter the path for your changelog file. +changeLogFile=example-changelog.json + +#### Enter the Target database 'url' information #### +liquibase.command.url=jdbc:h2:tcp://localhost:9090/mem:dev + +# Enter the username for your Target database. +liquibase.command.username: dbuser + +# Enter the password for your Target database. +liquibase.command.password: letmein + +#### Enter the Source Database 'referenceUrl' information #### +## The source database is the baseline or reference against which your target database is compared for diff/diffchangelog commands. + +# Enter URL for the source database +liquibase.command.referenceUrl: jdbc:h2:tcp://localhost:9090/mem:integration + +# Enter the username for your source database +liquibase.command.referenceUsername: dbuser + +# Enter the password for your source database +liquibase.command.referencePassword: letmein + +# Logging Configuration +# logLevel controls the amount of logging information generated. If not set, the default logLevel is INFO. +# Valid values, from least amount of logging to most, are: +# OFF, ERROR, WARN, INFO, DEBUG, TRACE, ALL +# If you are having problems, setting the logLevel to DEBUG and re-running the command can be helpful. +# logLevel: DEBUG + +# The logFile property controls where logging messages are sent. If this is not set, then logging messages are +# displayed on the console. If this is set, then messages will be sent to a file with the given name. +# logFile: liquibase.log + + +#### Liquibase Pro Key Information #### +# Learn more, contact support, or get or renew a Pro Key at https://www.liquibase.com/protrial +# liquibase.pro.licensekey: + +#### Liquibase Hub Information #### +# Liquibase Hub is a free secure SaaS portal providing status reporting, monitoring & insights +# into your Liquibase database release automation. +# https://hub.liquibase.com + +## Add your free Hub API key here +# liquibase.hub.apikey: +# liquibase.hub.mode:all + + + + +## Get documentation at docs.liquibase.com ## +## Get certified courses at learn.liquibase.com ## +## Get support at liquibase.com/support ## diff --git a/liquibase-core/src/main/resources/liquibase/examples/sql/blankchangelog.sql b/liquibase-core/src/main/resources/liquibase/examples/sql/blankchangelog.sql new file mode 100644 index 00000000000..b82062b1789 --- /dev/null +++ b/liquibase-core/src/main/resources/liquibase/examples/sql/blankchangelog.sql @@ -0,0 +1,12 @@ +--liquibase formatted sql +/* https://www.liquibase.org/documentation/sql_format.html */ + +--changeset authorname:1 +/* Insert SQL change objects here */ + + +--changeset authorname:2 +/* Insert SQL change objects here */ + + + diff --git a/liquibase-core/src/main/resources/liquibase/examples/sql/example-changelog.sql b/liquibase-core/src/main/resources/liquibase/examples/sql/example-changelog.sql new file mode 100644 index 00000000000..5e89d1b7aef --- /dev/null +++ b/liquibase-core/src/main/resources/liquibase/examples/sql/example-changelog.sql @@ -0,0 +1,23 @@ +--liquibase formatted sql + +--changeset your.name:1 +create table person ( + id int primary key, + name varchar(50) not null, + address1 varchar(50), + address2 varchar(50), + city varchar(30) +) + +--changeset your.name:2 +create table company ( + id int primary key, + name varchar(50) not null, + address1 varchar(50), + address2 varchar(50), + city varchar(30) +) + +--changeset other.dev:3 +alter table person add column country varchar(2) + diff --git a/liquibase-core/src/main/resources/liquibase/examples/sql/example-changeset.sql b/liquibase-core/src/main/resources/liquibase/examples/sql/example-changeset.sql new file mode 100644 index 00000000000..72fd788ec8e --- /dev/null +++ b/liquibase-core/src/main/resources/liquibase/examples/sql/example-changeset.sql @@ -0,0 +1,7 @@ +(example-changeset.sql) + +--changeset dev:1 +create table test1( + id int primary key, + name varchar(255) +); diff --git a/liquibase-core/src/main/resources/liquibase/examples/sql/liquibase.properties b/liquibase-core/src/main/resources/liquibase/examples/sql/liquibase.properties new file mode 100644 index 00000000000..1f30dc71cea --- /dev/null +++ b/liquibase-core/src/main/resources/liquibase/examples/sql/liquibase.properties @@ -0,0 +1,76 @@ +#### _ _ _ _ +## | | (_) (_) | +## | | _ __ _ _ _ _| |__ __ _ ___ ___ +## | | | |/ _` | | | | | '_ \ / _` / __|/ _ \ +## | |___| | (_| | |_| | | |_) | (_| \__ \ __/ +## \_____/_|\__, |\__,_|_|_.__/ \__,_|___/\___| +## | | +## |_| +## +## The liquibase.properties file stores properties which do not change often, +## such as database connection information. Properties stored here save time +## and reduce risk of mistyped command line arguments. +## Learn more: https://www.liquibase.org/documentation/config_properties.html +#### +#### +## Note about relative and absolute paths: +## The liquibase.properties file requires paths for some properties. +## The classpath is the path/to/resources (ex. src/main/resources). +## The changeLogFile path is relative to the classpath. +## The url H2 example below is relative to 'pwd' resource. +#### +# Enter the path for your changelog file. +changeLogFile=samplechangelog.h2.sql + +#### Enter the Target database 'url' information #### +liquibase.command.url=jdbc:h2:tcp://localhost:9090/mem:dev + +# Enter the username for your Target database. +liquibase.command.username: dbuser + +# Enter the password for your Target database. +liquibase.command.password: letmein + +#### Enter the Source Database 'referenceUrl' information #### +## The source database is the baseline or reference against which your target database is compared for diff/diffchangelog commands. + +# Enter URL for the source database +liquibase.command.referenceUrl: jdbc:h2:tcp://localhost:9090/mem:integration + +# Enter the username for your source database +liquibase.command.referenceUsername: dbuser + +# Enter the password for your source database +liquibase.command.referencePassword: letmein + +# Logging Configuration +# logLevel controls the amount of logging information generated. If not set, the default logLevel is INFO. +# Valid values, from least amount of logging to most, are: +# OFF, ERROR, WARN, INFO, DEBUG, TRACE, ALL +# If you are having problems, setting the logLevel to DEBUG and re-running the command can be helpful. +# logLevel: DEBUG + +# The logFile property controls where logging messages are sent. If this is not set, then logging messages are +# displayed on the console. If this is set, then messages will be sent to a file with the given name. +# logFile: liquibase.log + + +#### Liquibase Pro Key Information #### +# Learn more, contact support, or get or renew a Pro Key at https://www.liquibase.com/protrial +# liquibase.pro.licensekey: + +#### Liquibase Hub Information #### +# Liquibase Hub is a free secure SaaS portal providing status reporting, monitoring & insights +# into your Liquibase database release automation. +# https://hub.liquibase.com + +## Add your free Hub API key here +# liquibase.hub.apikey: +# liquibase.hub.mode:all + + + + +## Get documentation at docs.liquibase.com ## +## Get certified courses at learn.liquibase.com ## +## Get support at liquibase.com/support ## diff --git a/liquibase-core/src/main/resources/liquibase/examples/sql/liquibase.sqlplus.conf b/liquibase-core/src/main/resources/liquibase/examples/sql/liquibase.sqlplus.conf new file mode 100644 index 00000000000..f6afd2cbcb6 --- /dev/null +++ b/liquibase-core/src/main/resources/liquibase/examples/sql/liquibase.sqlplus.conf @@ -0,0 +1,47 @@ +#### _ _ _ _ _____ +## | | (_) (_) | | __ \ +## | | _ __ _ _ _ _| |__ __ _ ___ ___ | |__) | __ ___ +## | | | |/ _` | | | | | '_ \ / _` / __|/ _ \ | ___/ '__/ _ \ +## | |___| | (_| | |_| | | |_) | (_| \__ \ __/ | | | | | (_) | +## \_____/_|\__, |\__,_|_|_.__/ \__,_|___/\___| |_| |_| \___/ +## | | +## |_| +## +## The liquibase.sqlplus.conf file stores properties which are used during the +## execution of the Oracle SQLPLUS tool. +## Learn more: https://www.liquibase.org/documentation/config_properties.html +#### +#### +## Note about relative and absolute paths: +## The liquibase.sqlplus.path must be a valid path to the SQLPlUS executable. +## The liquibase.sqlplus.timeout value can be one of: +## -1 - disable the timeout +## Any integer value > 0 (measured in seconds) +## +#### + +# The full path to the SQLPLUS executable. +# Sample linux path +# liquibase.sqlplus.path=/apps/app/12.2.0.1.0/oracle/product/12.2.0.1.0/client_1/bin/sqlplus +# Sample windows path +# liquibase.sqlplus.path=c:\\oracle\\product\\11.2.0\\client_1\\bin\\sqlplus.exe + +# A valid timeout value for the execution of the SQLPLUS tool +liquibase.sqlplus.timeout=-1 + +# Flag to indicate whether or not to keep the temporary SQL file after execution of SQLPLUS. +# True = keep False = delete (default) +liquibase.sqlplus.keep.temp=true + +# OPTIONAL Flag to designate the location to store temporary SQL file after execution of SQLPLUS. +# Liquibase will attempt to use path exactly as entered, so please ensure it complies with your OS requirements. +# liquibase.sqlplus.keep.temp.path= + +# OPTIONAL Flag to designate the name of temporary SQL file after execution of SQLPLUS. +# Liquibase will attempt to use the name exactly as entered, so please ensure it complies with your OS requirements. +# liquibase.sqlplus.keep.temp.name= + +# OPTIONAL Args to pass directly to SQLPLUS. +# Learn about SQLPLUS args at https://docs.oracle.com/cd/B10501_01/server.920/a90842/ch4.htm +# Note: The delimiter for args is a space eg:" " and not "," or ";" separated. +# liquibase.sqlplus.args= diff --git a/liquibase-core/src/main/resources/liquibase/examples/start-h2 b/liquibase-core/src/main/resources/liquibase/examples/start-h2 new file mode 100644 index 00000000000..718090807bf --- /dev/null +++ b/liquibase-core/src/main/resources/liquibase/examples/start-h2 @@ -0,0 +1,41 @@ +#!/usr/bin/env bash + +if [ -z "${LIQUIBASE_HOME}" ]; then + #liquibase home is not set + + LIQUIBASE_PATH="$(which liquibase)" + + if [ -z "${LIQUIBASE_PATH}" ]; then + echo "Must set LIQUIBASE_HOME environment variable, or have liquibase in your PATH" + exit 1 + fi + + LIQUIBASE_HOME=$(dirname "$(which liquibase)") +fi + +if [ -z "${JAVA_HOME}" ]; then + #JAVA_HOME not set, try to find a bundled version + if [ -d "${LIQUIBASE_HOME}/jre" ]; then + JAVA_HOME="$LIQUIBASE_HOME/jre" + elif [ -d "${LIQUIBASE_HOME}/.install4j/jre.bundle/Contents/Home" ]; then + JAVA_HOME="${LIQUIBASE_HOME}/.install4j/jre.bundle/Contents/Home" + fi +fi + +if [ -z "${JAVA_HOME}" ]; then + JAVA_PATH="$(which java)" + + if [ -z "${JAVA_PATH}" ]; then + echo "Cannot find java in your path. Install java or use the JAVA_HOME environment variable" + + exit 1 + fi +else + #Use path in JAVA_HOME + JAVA_PATH="${JAVA_HOME}/bin/java" +fi + + +# echo "${JAVA_PATH}" -cp "${LIQUIBASE_HOME}/lib/h2-1.4.200.jar:${LIQUIBASE_HOME}/liquibase.jar" liquibase.example.StartH2Main + +"${JAVA_PATH}" -cp "${LIQUIBASE_HOME}/lib/h2-1.4.200.jar:${LIQUIBASE_HOME}/liquibase.jar" liquibase.example.StartH2Main diff --git a/liquibase-core/src/main/resources/liquibase/examples/start-h2.bat b/liquibase-core/src/main/resources/liquibase/examples/start-h2.bat new file mode 100644 index 00000000000..21603ba57e4 --- /dev/null +++ b/liquibase-core/src/main/resources/liquibase/examples/start-h2.bat @@ -0,0 +1,34 @@ +@echo off +if "%OS%" == "Windows_NT" setlocal + +setlocal enabledelayedexpansion + +rem %~dp0 is expanded pathname of the current script under NT +rem %~p0 is the directory of the current script + +if exist %~p0\..\liquibase.jar SET LIQUIBASE_HOME="%~p0\.." + +if "%LIQUIBASE_HOME%"=="" ( + FOR /F "tokens=* USEBACKQ" %%g IN (`where liquibase.bat`) do (SET "LIQUIBASE_HOME=%%~dpg") +) + +if "%LIQUIBASE_HOME%"=="" ( + echo "Must set LIQUIBASE_HOME environment variable, or have liquibase.bat in your PATH" + exit /B 1 +) + +if "%JAVA_HOME%"=="" ( + + rem check for jre dir in liquibase_home + if NOT "%LIQUIBASE_HOME%"=="" if exist "%LIQUIBASE_HOME%\jre" ( + set JAVA_HOME=%LIQUIBASE_HOME%\jre + ) +) + +if "%JAVA_HOME%"=="" ( + set JAVA_PATH=java +) else ( + set JAVA_PATH=%JAVA_HOME%\bin\java +) + +"%JAVA_PATH%" -cp "%LIQUIBASE_HOME%\lib\h2-1.4.200.jar;%LIQUIBASE_HOME%\liquibase.jar" liquibase.example.StartH2Main diff --git a/liquibase-core/src/main/resources/liquibase/examples/xml/blank.changelog.xml b/liquibase-core/src/main/resources/liquibase/examples/xml/blank.changelog.xml new file mode 100644 index 00000000000..fc9b1d763d1 --- /dev/null +++ b/liquibase-core/src/main/resources/liquibase/examples/xml/blank.changelog.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + diff --git a/liquibase-core/src/main/resources/liquibase/examples/xml/example-changelog.xml b/liquibase-core/src/main/resources/liquibase/examples/xml/example-changelog.xml new file mode 100644 index 00000000000..cee6908804b --- /dev/null +++ b/liquibase-core/src/main/resources/liquibase/examples/xml/example-changelog.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/liquibase-core/src/main/resources/liquibase/examples/xml/example-changeset.xml b/liquibase-core/src/main/resources/liquibase/examples/xml/example-changeset.xml new file mode 100644 index 00000000000..4f162b30621 --- /dev/null +++ b/liquibase-core/src/main/resources/liquibase/examples/xml/example-changeset.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/liquibase-core/src/main/resources/liquibase/examples/xml/liquibase.properties b/liquibase-core/src/main/resources/liquibase/examples/xml/liquibase.properties new file mode 100644 index 00000000000..30b30d9ee9b --- /dev/null +++ b/liquibase-core/src/main/resources/liquibase/examples/xml/liquibase.properties @@ -0,0 +1,76 @@ +#### _ _ _ _ +## | | (_) (_) | +## | | _ __ _ _ _ _| |__ __ _ ___ ___ +## | | | |/ _` | | | | | '_ \ / _` / __|/ _ \ +## | |___| | (_| | |_| | | |_) | (_| \__ \ __/ +## \_____/_|\__, |\__,_|_|_.__/ \__,_|___/\___| +## | | +## |_| +## +## The liquibase.properties file stores properties which do not change often, +## such as database connection information. Properties stored here save time +## and reduce risk of mistyped command line arguments. +## Learn more: https://www.liquibase.org/documentation/config_properties.html +#### +#### +## Note about relative and absolute paths: +## The liquibase.properties file requires paths for some properties. +## The classpath is the path/to/resources (ex. src/main/resources). +## The changeLogFile path is relative to the classpath. +## The url H2 example below is relative to 'pwd' resource. +#### +# Enter the path for your changelog file. +changeLogFile=example-changelog.xml + +#### Enter the Target database 'url' information #### +liquibase.command.url=jdbc:h2:tcp://localhost:9090/mem:dev + +# Enter the username for your Target database. +liquibase.command.username: dbuser + +# Enter the password for your Target database. +liquibase.command.password: letmein + +#### Enter the Source Database 'referenceUrl' information #### +## The source database is the baseline or reference against which your target database is compared for diff/diffchangelog commands. + +# Enter URL for the source database +liquibase.command.referenceUrl: jdbc:h2:tcp://localhost:9090/mem:integration + +# Enter the username for your source database +liquibase.command.referenceUsername: dbuser + +# Enter the password for your source database +liquibase.command.referencePassword: letmein + +# Logging Configuration +# logLevel controls the amount of logging information generated. If not set, the default logLevel is INFO. +# Valid values, from least amount of logging to most, are: +# OFF, ERROR, WARN, INFO, DEBUG, TRACE, ALL +# If you are having problems, setting the logLevel to DEBUG and re-running the command can be helpful. +# logLevel: DEBUG + +# The logFile property controls where logging messages are sent. If this is not set, then logging messages are +# displayed on the console. If this is set, then messages will be sent to a file with the given name. +# logFile: liquibase.log + + +#### Liquibase Pro Key Information #### +# Learn more, contact support, or get or renew a Pro Key at https://www.liquibase.com/protrial +# liquibase.pro.licensekey: + +#### Liquibase Hub Information #### +# Liquibase Hub is a free secure SaaS portal providing status reporting, monitoring & insights +# into your Liquibase database release automation. +# https://hub.liquibase.com + +## Add your free Hub API key here +# liquibase.hub.apikey: +# liquibase.hub.mode:all + + + + +## Get documentation at docs.liquibase.com ## +## Get certified courses at learn.liquibase.com ## +## Get support at liquibase.com/support ## diff --git a/liquibase-core/src/main/resources/liquibase/examples/xml/liquibase.sqlplus.conf b/liquibase-core/src/main/resources/liquibase/examples/xml/liquibase.sqlplus.conf new file mode 100644 index 00000000000..f6afd2cbcb6 --- /dev/null +++ b/liquibase-core/src/main/resources/liquibase/examples/xml/liquibase.sqlplus.conf @@ -0,0 +1,47 @@ +#### _ _ _ _ _____ +## | | (_) (_) | | __ \ +## | | _ __ _ _ _ _| |__ __ _ ___ ___ | |__) | __ ___ +## | | | |/ _` | | | | | '_ \ / _` / __|/ _ \ | ___/ '__/ _ \ +## | |___| | (_| | |_| | | |_) | (_| \__ \ __/ | | | | | (_) | +## \_____/_|\__, |\__,_|_|_.__/ \__,_|___/\___| |_| |_| \___/ +## | | +## |_| +## +## The liquibase.sqlplus.conf file stores properties which are used during the +## execution of the Oracle SQLPLUS tool. +## Learn more: https://www.liquibase.org/documentation/config_properties.html +#### +#### +## Note about relative and absolute paths: +## The liquibase.sqlplus.path must be a valid path to the SQLPlUS executable. +## The liquibase.sqlplus.timeout value can be one of: +## -1 - disable the timeout +## Any integer value > 0 (measured in seconds) +## +#### + +# The full path to the SQLPLUS executable. +# Sample linux path +# liquibase.sqlplus.path=/apps/app/12.2.0.1.0/oracle/product/12.2.0.1.0/client_1/bin/sqlplus +# Sample windows path +# liquibase.sqlplus.path=c:\\oracle\\product\\11.2.0\\client_1\\bin\\sqlplus.exe + +# A valid timeout value for the execution of the SQLPLUS tool +liquibase.sqlplus.timeout=-1 + +# Flag to indicate whether or not to keep the temporary SQL file after execution of SQLPLUS. +# True = keep False = delete (default) +liquibase.sqlplus.keep.temp=true + +# OPTIONAL Flag to designate the location to store temporary SQL file after execution of SQLPLUS. +# Liquibase will attempt to use path exactly as entered, so please ensure it complies with your OS requirements. +# liquibase.sqlplus.keep.temp.path= + +# OPTIONAL Flag to designate the name of temporary SQL file after execution of SQLPLUS. +# Liquibase will attempt to use the name exactly as entered, so please ensure it complies with your OS requirements. +# liquibase.sqlplus.keep.temp.name= + +# OPTIONAL Args to pass directly to SQLPLUS. +# Learn about SQLPLUS args at https://docs.oracle.com/cd/B10501_01/server.920/a90842/ch4.htm +# Note: The delimiter for args is a space eg:" " and not "," or ";" separated. +# liquibase.sqlplus.args= diff --git a/liquibase-core/src/main/resources/liquibase/examples/yaml/example-changelog.yaml b/liquibase-core/src/main/resources/liquibase/examples/yaml/example-changelog.yaml new file mode 100644 index 00000000000..0ca1741a930 --- /dev/null +++ b/liquibase-core/src/main/resources/liquibase/examples/yaml/example-changelog.yaml @@ -0,0 +1,48 @@ +databaseChangeLog: +- changeLogId: 455d447e-d2c9-42f1-b283-0cc524934a51 +- changeSet: + id: 1 + author: your.name + changes: + - createTable: + tableName: person + columns: + - column: + name: id + type: int + autoIncrement: true + constraints: + primaryKey: true + nullable: false + - column: + name: name + type: varchar(50) + +- changeSet: + id: 2 + author: your.name + changes: + - createTable: + tableName: company + columns: + - column: + name: id + type: int + autoIncrement: true + constraints: + primaryKey: true + nullable: false + - column: + name: name + type: varchar(50) + +- changeSet: + id: 3 + author: your.name + changes: + - addColumn: + tableName: company + columns: + - column: + name: id + type: varchar(2) diff --git a/liquibase-core/src/main/resources/liquibase/examples/yaml/example-changeset.yaml b/liquibase-core/src/main/resources/liquibase/examples/yaml/example-changeset.yaml new file mode 100644 index 00000000000..ac332f2bf23 --- /dev/null +++ b/liquibase-core/src/main/resources/liquibase/examples/yaml/example-changeset.yaml @@ -0,0 +1,17 @@ + - changeSet: + id: 1 + author: dev + changes: + - createTable: + tableName: person + columns: + - column: + name: id + type: int + autoIncrement: true + constraints: + primaryKey: true + nullable: false + - column: + name: name + type: varchar(255) diff --git a/liquibase-core/src/main/resources/liquibase/examples/yaml/liquibase.properties b/liquibase-core/src/main/resources/liquibase/examples/yaml/liquibase.properties new file mode 100644 index 00000000000..f964af53f06 --- /dev/null +++ b/liquibase-core/src/main/resources/liquibase/examples/yaml/liquibase.properties @@ -0,0 +1,76 @@ +#### _ _ _ _ +## | | (_) (_) | +## | | _ __ _ _ _ _| |__ __ _ ___ ___ +## | | | |/ _` | | | | | '_ \ / _` / __|/ _ \ +## | |___| | (_| | |_| | | |_) | (_| \__ \ __/ +## \_____/_|\__, |\__,_|_|_.__/ \__,_|___/\___| +## | | +## |_| +## +## The liquibase.properties file stores properties which do not change often, +## such as database connection information. Properties stored here save time +## and reduce risk of mistyped command line arguments. +## Learn more: https://www.liquibase.org/documentation/config_properties.html +#### +#### +## Note about relative and absolute paths: +## The liquibase.properties file requires paths for some properties. +## The classpath is the path/to/resources (ex. src/main/resources). +## The changeLogFile path is relative to the classpath. +## The url H2 example below is relative to 'pwd' resource. +#### +# Enter the path for your changelog file. +changeLogFile=example-changelog.yaml + +#### Enter the Target database 'url' information #### +liquibase.command.url=jdbc:h2:tcp://localhost:9090/mem:dev + +# Enter the username for your Target database. +liquibase.command.username: dbuser + +# Enter the password for your Target database. +liquibase.command.password: letmein + +#### Enter the Source Database 'referenceUrl' information #### +## The source database is the baseline or reference against which your target database is compared for diff/diffchangelog commands. + +# Enter URL for the source database +liquibase.command.referenceUrl: jdbc:h2:tcp://localhost:9090/mem:integration + +# Enter the username for your source database +liquibase.command.referenceUsername: dbuser + +# Enter the password for your source database +liquibase.command.referencePassword: letmein + +# Logging Configuration +# logLevel controls the amount of logging information generated. If not set, the default logLevel is INFO. +# Valid values, from least amount of logging to most, are: +# OFF, ERROR, WARN, INFO, DEBUG, TRACE, ALL +# If you are having problems, setting the logLevel to DEBUG and re-running the command can be helpful. +# logLevel: DEBUG + +# The logFile property controls where logging messages are sent. If this is not set, then logging messages are +# displayed on the console. If this is set, then messages will be sent to a file with the given name. +# logFile: liquibase.log + + +#### Liquibase Pro Key Information #### +# Learn more, contact support, or get or renew a Pro Key at https://www.liquibase.com/protrial +# liquibase.pro.licensekey: + +#### Liquibase Hub Information #### +# Liquibase Hub is a free secure SaaS portal providing status reporting, monitoring & insights +# into your Liquibase database release automation. +# https://hub.liquibase.com + +## Add your free Hub API key here +# liquibase.hub.apikey: +# liquibase.hub.mode:all + + + + +## Get documentation at docs.liquibase.com ## +## Get certified courses at learn.liquibase.com ## +## Get support at liquibase.com/support ## diff --git a/liquibase-dist/src/main/assembly/assembly-bin.xml b/liquibase-dist/src/main/assembly/assembly-bin.xml index 6255cda5e2b..1370859aad1 100644 --- a/liquibase-dist/src/main/assembly/assembly-bin.xml +++ b/liquibase-dist/src/main/assembly/assembly-bin.xml @@ -47,6 +47,13 @@ **/* + + ${project.basedir}/../liquibase-core/target/classes/liquibase/examples + examples + + **/* + + diff --git a/liquibase-dist/src/main/install4j/liquibase.install4j b/liquibase-dist/src/main/install4j/liquibase.install4j index e13cc83226d..2ed315b28d9 100644 --- a/liquibase-dist/src/main/install4j/liquibase.install4j +++ b/liquibase-dist/src/main/install4j/liquibase.install4j @@ -1,7 +1,7 @@ - + From 44028286c910a9ffda3d488ad9a10042c576492d Mon Sep 17 00:00:00 2001 From: Wesley Willard Date: Thu, 9 Dec 2021 16:27:15 -0600 Subject: [PATCH 06/37] LB-2177 Some corrections --- .../examples/sql/liquibase.properties | 2 +- .../examples/sql/blankchangelog.h2.sql | 12 --- .../archive/examples/sql/liquibase.properties | 76 ------------------- .../examples/sql/liquibase.sqlplus.conf | 47 ------------ .../examples/sql/samplechangelog.h2.sql | 23 ------ .../archive/examples/xml/blank.changelog.xml | 18 ----- .../archive/examples/xml/liquibase.properties | 76 ------------------- .../examples/xml/liquibase.sqlplus.conf | 47 ------------ .../archive/examples/xml/sample.changelog.xml | 43 ----------- 9 files changed, 1 insertion(+), 343 deletions(-) delete mode 100644 liquibase-dist/src/main/archive/examples/sql/blankchangelog.h2.sql delete mode 100644 liquibase-dist/src/main/archive/examples/sql/liquibase.properties delete mode 100644 liquibase-dist/src/main/archive/examples/sql/liquibase.sqlplus.conf delete mode 100644 liquibase-dist/src/main/archive/examples/sql/samplechangelog.h2.sql delete mode 100644 liquibase-dist/src/main/archive/examples/xml/blank.changelog.xml delete mode 100644 liquibase-dist/src/main/archive/examples/xml/liquibase.properties delete mode 100644 liquibase-dist/src/main/archive/examples/xml/liquibase.sqlplus.conf delete mode 100644 liquibase-dist/src/main/archive/examples/xml/sample.changelog.xml diff --git a/liquibase-core/src/main/resources/liquibase/examples/sql/liquibase.properties b/liquibase-core/src/main/resources/liquibase/examples/sql/liquibase.properties index 1f30dc71cea..d15b155e6c5 100644 --- a/liquibase-core/src/main/resources/liquibase/examples/sql/liquibase.properties +++ b/liquibase-core/src/main/resources/liquibase/examples/sql/liquibase.properties @@ -20,7 +20,7 @@ ## The url H2 example below is relative to 'pwd' resource. #### # Enter the path for your changelog file. -changeLogFile=samplechangelog.h2.sql +changeLogFile=example-changelog.sql #### Enter the Target database 'url' information #### liquibase.command.url=jdbc:h2:tcp://localhost:9090/mem:dev diff --git a/liquibase-dist/src/main/archive/examples/sql/blankchangelog.h2.sql b/liquibase-dist/src/main/archive/examples/sql/blankchangelog.h2.sql deleted file mode 100644 index b82062b1789..00000000000 --- a/liquibase-dist/src/main/archive/examples/sql/blankchangelog.h2.sql +++ /dev/null @@ -1,12 +0,0 @@ ---liquibase formatted sql -/* https://www.liquibase.org/documentation/sql_format.html */ - ---changeset authorname:1 -/* Insert SQL change objects here */ - - ---changeset authorname:2 -/* Insert SQL change objects here */ - - - diff --git a/liquibase-dist/src/main/archive/examples/sql/liquibase.properties b/liquibase-dist/src/main/archive/examples/sql/liquibase.properties deleted file mode 100644 index 1f30dc71cea..00000000000 --- a/liquibase-dist/src/main/archive/examples/sql/liquibase.properties +++ /dev/null @@ -1,76 +0,0 @@ -#### _ _ _ _ -## | | (_) (_) | -## | | _ __ _ _ _ _| |__ __ _ ___ ___ -## | | | |/ _` | | | | | '_ \ / _` / __|/ _ \ -## | |___| | (_| | |_| | | |_) | (_| \__ \ __/ -## \_____/_|\__, |\__,_|_|_.__/ \__,_|___/\___| -## | | -## |_| -## -## The liquibase.properties file stores properties which do not change often, -## such as database connection information. Properties stored here save time -## and reduce risk of mistyped command line arguments. -## Learn more: https://www.liquibase.org/documentation/config_properties.html -#### -#### -## Note about relative and absolute paths: -## The liquibase.properties file requires paths for some properties. -## The classpath is the path/to/resources (ex. src/main/resources). -## The changeLogFile path is relative to the classpath. -## The url H2 example below is relative to 'pwd' resource. -#### -# Enter the path for your changelog file. -changeLogFile=samplechangelog.h2.sql - -#### Enter the Target database 'url' information #### -liquibase.command.url=jdbc:h2:tcp://localhost:9090/mem:dev - -# Enter the username for your Target database. -liquibase.command.username: dbuser - -# Enter the password for your Target database. -liquibase.command.password: letmein - -#### Enter the Source Database 'referenceUrl' information #### -## The source database is the baseline or reference against which your target database is compared for diff/diffchangelog commands. - -# Enter URL for the source database -liquibase.command.referenceUrl: jdbc:h2:tcp://localhost:9090/mem:integration - -# Enter the username for your source database -liquibase.command.referenceUsername: dbuser - -# Enter the password for your source database -liquibase.command.referencePassword: letmein - -# Logging Configuration -# logLevel controls the amount of logging information generated. If not set, the default logLevel is INFO. -# Valid values, from least amount of logging to most, are: -# OFF, ERROR, WARN, INFO, DEBUG, TRACE, ALL -# If you are having problems, setting the logLevel to DEBUG and re-running the command can be helpful. -# logLevel: DEBUG - -# The logFile property controls where logging messages are sent. If this is not set, then logging messages are -# displayed on the console. If this is set, then messages will be sent to a file with the given name. -# logFile: liquibase.log - - -#### Liquibase Pro Key Information #### -# Learn more, contact support, or get or renew a Pro Key at https://www.liquibase.com/protrial -# liquibase.pro.licensekey: - -#### Liquibase Hub Information #### -# Liquibase Hub is a free secure SaaS portal providing status reporting, monitoring & insights -# into your Liquibase database release automation. -# https://hub.liquibase.com - -## Add your free Hub API key here -# liquibase.hub.apikey: -# liquibase.hub.mode:all - - - - -## Get documentation at docs.liquibase.com ## -## Get certified courses at learn.liquibase.com ## -## Get support at liquibase.com/support ## diff --git a/liquibase-dist/src/main/archive/examples/sql/liquibase.sqlplus.conf b/liquibase-dist/src/main/archive/examples/sql/liquibase.sqlplus.conf deleted file mode 100644 index f6afd2cbcb6..00000000000 --- a/liquibase-dist/src/main/archive/examples/sql/liquibase.sqlplus.conf +++ /dev/null @@ -1,47 +0,0 @@ -#### _ _ _ _ _____ -## | | (_) (_) | | __ \ -## | | _ __ _ _ _ _| |__ __ _ ___ ___ | |__) | __ ___ -## | | | |/ _` | | | | | '_ \ / _` / __|/ _ \ | ___/ '__/ _ \ -## | |___| | (_| | |_| | | |_) | (_| \__ \ __/ | | | | | (_) | -## \_____/_|\__, |\__,_|_|_.__/ \__,_|___/\___| |_| |_| \___/ -## | | -## |_| -## -## The liquibase.sqlplus.conf file stores properties which are used during the -## execution of the Oracle SQLPLUS tool. -## Learn more: https://www.liquibase.org/documentation/config_properties.html -#### -#### -## Note about relative and absolute paths: -## The liquibase.sqlplus.path must be a valid path to the SQLPlUS executable. -## The liquibase.sqlplus.timeout value can be one of: -## -1 - disable the timeout -## Any integer value > 0 (measured in seconds) -## -#### - -# The full path to the SQLPLUS executable. -# Sample linux path -# liquibase.sqlplus.path=/apps/app/12.2.0.1.0/oracle/product/12.2.0.1.0/client_1/bin/sqlplus -# Sample windows path -# liquibase.sqlplus.path=c:\\oracle\\product\\11.2.0\\client_1\\bin\\sqlplus.exe - -# A valid timeout value for the execution of the SQLPLUS tool -liquibase.sqlplus.timeout=-1 - -# Flag to indicate whether or not to keep the temporary SQL file after execution of SQLPLUS. -# True = keep False = delete (default) -liquibase.sqlplus.keep.temp=true - -# OPTIONAL Flag to designate the location to store temporary SQL file after execution of SQLPLUS. -# Liquibase will attempt to use path exactly as entered, so please ensure it complies with your OS requirements. -# liquibase.sqlplus.keep.temp.path= - -# OPTIONAL Flag to designate the name of temporary SQL file after execution of SQLPLUS. -# Liquibase will attempt to use the name exactly as entered, so please ensure it complies with your OS requirements. -# liquibase.sqlplus.keep.temp.name= - -# OPTIONAL Args to pass directly to SQLPLUS. -# Learn about SQLPLUS args at https://docs.oracle.com/cd/B10501_01/server.920/a90842/ch4.htm -# Note: The delimiter for args is a space eg:" " and not "," or ";" separated. -# liquibase.sqlplus.args= diff --git a/liquibase-dist/src/main/archive/examples/sql/samplechangelog.h2.sql b/liquibase-dist/src/main/archive/examples/sql/samplechangelog.h2.sql deleted file mode 100644 index 5e89d1b7aef..00000000000 --- a/liquibase-dist/src/main/archive/examples/sql/samplechangelog.h2.sql +++ /dev/null @@ -1,23 +0,0 @@ ---liquibase formatted sql - ---changeset your.name:1 -create table person ( - id int primary key, - name varchar(50) not null, - address1 varchar(50), - address2 varchar(50), - city varchar(30) -) - ---changeset your.name:2 -create table company ( - id int primary key, - name varchar(50) not null, - address1 varchar(50), - address2 varchar(50), - city varchar(30) -) - ---changeset other.dev:3 -alter table person add column country varchar(2) - diff --git a/liquibase-dist/src/main/archive/examples/xml/blank.changelog.xml b/liquibase-dist/src/main/archive/examples/xml/blank.changelog.xml deleted file mode 100644 index fc9b1d763d1..00000000000 --- a/liquibase-dist/src/main/archive/examples/xml/blank.changelog.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - diff --git a/liquibase-dist/src/main/archive/examples/xml/liquibase.properties b/liquibase-dist/src/main/archive/examples/xml/liquibase.properties deleted file mode 100644 index fa7fb0ff901..00000000000 --- a/liquibase-dist/src/main/archive/examples/xml/liquibase.properties +++ /dev/null @@ -1,76 +0,0 @@ -#### _ _ _ _ -## | | (_) (_) | -## | | _ __ _ _ _ _| |__ __ _ ___ ___ -## | | | |/ _` | | | | | '_ \ / _` / __|/ _ \ -## | |___| | (_| | |_| | | |_) | (_| \__ \ __/ -## \_____/_|\__, |\__,_|_|_.__/ \__,_|___/\___| -## | | -## |_| -## -## The liquibase.properties file stores properties which do not change often, -## such as database connection information. Properties stored here save time -## and reduce risk of mistyped command line arguments. -## Learn more: https://www.liquibase.org/documentation/config_properties.html -#### -#### -## Note about relative and absolute paths: -## The liquibase.properties file requires paths for some properties. -## The classpath is the path/to/resources (ex. src/main/resources). -## The changeLogFile path is relative to the classpath. -## The url H2 example below is relative to 'pwd' resource. -#### -# Enter the path for your changelog file. -changeLogFile=sample.changelog.xml - -#### Enter the Target database 'url' information #### -liquibase.command.url=jdbc:h2:tcp://localhost:9090/mem:dev - -# Enter the username for your Target database. -liquibase.command.username: dbuser - -# Enter the password for your Target database. -liquibase.command.password: letmein - -#### Enter the Source Database 'referenceUrl' information #### -## The source database is the baseline or reference against which your target database is compared for diff/diffchangelog commands. - -# Enter URL for the source database -liquibase.command.referenceUrl: jdbc:h2:tcp://localhost:9090/mem:integration - -# Enter the username for your source database -liquibase.command.referenceUsername: dbuser - -# Enter the password for your source database -liquibase.command.referencePassword: letmein - -# Logging Configuration -# logLevel controls the amount of logging information generated. If not set, the default logLevel is INFO. -# Valid values, from least amount of logging to most, are: -# OFF, ERROR, WARN, INFO, DEBUG, TRACE, ALL -# If you are having problems, setting the logLevel to DEBUG and re-running the command can be helpful. -# logLevel: DEBUG - -# The logFile property controls where logging messages are sent. If this is not set, then logging messages are -# displayed on the console. If this is set, then messages will be sent to a file with the given name. -# logFile: liquibase.log - - -#### Liquibase Pro Key Information #### -# Learn more, contact support, or get or renew a Pro Key at https://www.liquibase.com/protrial -# liquibase.pro.licensekey: - -#### Liquibase Hub Information #### -# Liquibase Hub is a free secure SaaS portal providing status reporting, monitoring & insights -# into your Liquibase database release automation. -# https://hub.liquibase.com - -## Add your free Hub API key here -# liquibase.hub.apikey: -# liquibase.hub.mode:all - - - - -## Get documentation at docs.liquibase.com ## -## Get certified courses at learn.liquibase.com ## -## Get support at liquibase.com/support ## diff --git a/liquibase-dist/src/main/archive/examples/xml/liquibase.sqlplus.conf b/liquibase-dist/src/main/archive/examples/xml/liquibase.sqlplus.conf deleted file mode 100644 index f6afd2cbcb6..00000000000 --- a/liquibase-dist/src/main/archive/examples/xml/liquibase.sqlplus.conf +++ /dev/null @@ -1,47 +0,0 @@ -#### _ _ _ _ _____ -## | | (_) (_) | | __ \ -## | | _ __ _ _ _ _| |__ __ _ ___ ___ | |__) | __ ___ -## | | | |/ _` | | | | | '_ \ / _` / __|/ _ \ | ___/ '__/ _ \ -## | |___| | (_| | |_| | | |_) | (_| \__ \ __/ | | | | | (_) | -## \_____/_|\__, |\__,_|_|_.__/ \__,_|___/\___| |_| |_| \___/ -## | | -## |_| -## -## The liquibase.sqlplus.conf file stores properties which are used during the -## execution of the Oracle SQLPLUS tool. -## Learn more: https://www.liquibase.org/documentation/config_properties.html -#### -#### -## Note about relative and absolute paths: -## The liquibase.sqlplus.path must be a valid path to the SQLPlUS executable. -## The liquibase.sqlplus.timeout value can be one of: -## -1 - disable the timeout -## Any integer value > 0 (measured in seconds) -## -#### - -# The full path to the SQLPLUS executable. -# Sample linux path -# liquibase.sqlplus.path=/apps/app/12.2.0.1.0/oracle/product/12.2.0.1.0/client_1/bin/sqlplus -# Sample windows path -# liquibase.sqlplus.path=c:\\oracle\\product\\11.2.0\\client_1\\bin\\sqlplus.exe - -# A valid timeout value for the execution of the SQLPLUS tool -liquibase.sqlplus.timeout=-1 - -# Flag to indicate whether or not to keep the temporary SQL file after execution of SQLPLUS. -# True = keep False = delete (default) -liquibase.sqlplus.keep.temp=true - -# OPTIONAL Flag to designate the location to store temporary SQL file after execution of SQLPLUS. -# Liquibase will attempt to use path exactly as entered, so please ensure it complies with your OS requirements. -# liquibase.sqlplus.keep.temp.path= - -# OPTIONAL Flag to designate the name of temporary SQL file after execution of SQLPLUS. -# Liquibase will attempt to use the name exactly as entered, so please ensure it complies with your OS requirements. -# liquibase.sqlplus.keep.temp.name= - -# OPTIONAL Args to pass directly to SQLPLUS. -# Learn about SQLPLUS args at https://docs.oracle.com/cd/B10501_01/server.920/a90842/ch4.htm -# Note: The delimiter for args is a space eg:" " and not "," or ";" separated. -# liquibase.sqlplus.args= diff --git a/liquibase-dist/src/main/archive/examples/xml/sample.changelog.xml b/liquibase-dist/src/main/archive/examples/xml/sample.changelog.xml deleted file mode 100644 index cee6908804b..00000000000 --- a/liquibase-dist/src/main/archive/examples/xml/sample.changelog.xml +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From 3432f049f7166fa6dbe79f4207cb61d12d7e8b53 Mon Sep 17 00:00:00 2001 From: Wesley Willard Date: Thu, 9 Dec 2021 16:35:11 -0600 Subject: [PATCH 07/37] LB-2177 Remove changelogId from example --- .../resources/liquibase/examples/yaml/example-changelog.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/liquibase-core/src/main/resources/liquibase/examples/yaml/example-changelog.yaml b/liquibase-core/src/main/resources/liquibase/examples/yaml/example-changelog.yaml index 0ca1741a930..1334cb9795c 100644 --- a/liquibase-core/src/main/resources/liquibase/examples/yaml/example-changelog.yaml +++ b/liquibase-core/src/main/resources/liquibase/examples/yaml/example-changelog.yaml @@ -1,5 +1,4 @@ databaseChangeLog: -- changeLogId: 455d447e-d2c9-42f1-b283-0cc524934a51 - changeSet: id: 1 author: your.name From a69006ce0fcfc3e33a993e854cb78cebe5846ff1 Mon Sep 17 00:00:00 2001 From: Wesley Willard Date: Fri, 10 Dec 2021 11:32:07 -0600 Subject: [PATCH 08/37] LB-2177 More files --- .../examples/json/blank.changelog.json | 18 ++++++++++++++++++ ...{blankchangelog.sql => blank.changelog.sql} | 0 .../liquibase/examples/xml/blank.changelog.xml | 4 ++-- .../examples/yaml/blank.changelog.yaml | 12 ++++++++++++ 4 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 liquibase-core/src/main/resources/liquibase/examples/json/blank.changelog.json rename liquibase-core/src/main/resources/liquibase/examples/sql/{blankchangelog.sql => blank.changelog.sql} (100%) create mode 100644 liquibase-core/src/main/resources/liquibase/examples/yaml/blank.changelog.yaml diff --git a/liquibase-core/src/main/resources/liquibase/examples/json/blank.changelog.json b/liquibase-core/src/main/resources/liquibase/examples/json/blank.changelog.json new file mode 100644 index 00000000000..d912de64160 --- /dev/null +++ b/liquibase-core/src/main/resources/liquibase/examples/json/blank.changelog.json @@ -0,0 +1,18 @@ +{ "databaseChangeLog": [ + "changeset": { + "id": "1", + "author": "your.name", + "changes": [ + { + } + ] + }, + "changeset": { + "id": "2", + "author": "your.name", + "changes": [ + { + } + ] + } +]} diff --git a/liquibase-core/src/main/resources/liquibase/examples/sql/blankchangelog.sql b/liquibase-core/src/main/resources/liquibase/examples/sql/blank.changelog.sql similarity index 100% rename from liquibase-core/src/main/resources/liquibase/examples/sql/blankchangelog.sql rename to liquibase-core/src/main/resources/liquibase/examples/sql/blank.changelog.sql diff --git a/liquibase-core/src/main/resources/liquibase/examples/xml/blank.changelog.xml b/liquibase-core/src/main/resources/liquibase/examples/xml/blank.changelog.xml index fc9b1d763d1..61e339e5c36 100644 --- a/liquibase-core/src/main/resources/liquibase/examples/xml/blank.changelog.xml +++ b/liquibase-core/src/main/resources/liquibase/examples/xml/blank.changelog.xml @@ -8,11 +8,11 @@ http://www.liquibase.org/xml/ns/pro http://www.liquibase.org/xml/ns/pro/liquibase-pro-4.6.xsd "> - + - + diff --git a/liquibase-core/src/main/resources/liquibase/examples/yaml/blank.changelog.yaml b/liquibase-core/src/main/resources/liquibase/examples/yaml/blank.changelog.yaml new file mode 100644 index 00000000000..2a45be37f4c --- /dev/null +++ b/liquibase-core/src/main/resources/liquibase/examples/yaml/blank.changelog.yaml @@ -0,0 +1,12 @@ +databaseChangeLog: +- changeSet: + id: 1 + author: your.name + changes: + # Insert Yaml change objects here https://www.liquibase.org/documentation/xml_format.html + +- changeSet: + id: 2 + author: your.name + changes: + # Insert Yaml change objects here https://www.liquibase.org/documentation/xml_format.html From 145ad997db6bff86e0631508a9285c9aad1bb46f Mon Sep 17 00:00:00 2001 From: Wesley Willard Date: Mon, 13 Dec 2021 09:50:52 -0600 Subject: [PATCH 09/37] Fixed issues with sample JSON changelog DAT-8766 --- .../examples/json/example-changelog.json | 221 +++++++++--------- 1 file changed, 110 insertions(+), 111 deletions(-) diff --git a/liquibase-core/src/main/resources/liquibase/examples/json/example-changelog.json b/liquibase-core/src/main/resources/liquibase/examples/json/example-changelog.json index 3cef3ef10b4..de1539e031d 100644 --- a/liquibase-core/src/main/resources/liquibase/examples/json/example-changelog.json +++ b/liquibase-core/src/main/resources/liquibase/examples/json/example-changelog.json @@ -1,120 +1,119 @@ { "databaseChangeLog": [ - "changeset": { - "id": "1", - "author": "your.name", - "changes": [ + { + "changeSet": { + "id": "1", + "author": "your.name", + "changes": [ { - "createTable": - { - "tablename": "person", - "columns": [ - { - "column": { - "name": "id", - "type": "int", - "autoincrement": true, - "constraints": { - "primarykey": true, - "nullable": false - } - } - }, - { - "column": { - "name": "name", - "type": "varchar(50)" - } - }, - { - "column": { - "name": "addresss1", - "type": "varchar(50)" - } - }, - { - "column": { - "name": "addresss2", - "type": "varchar(50)" - } - }, - { - "column": { - "name": "city", - "type": "varchar(30)" - } - } - ] - } + "createTable": { + "tablename": "person", + "columns": [ + { + "column": { + "name": "id", + "type": "int", + "autoincrement": true, + "constraints": { + "primarykey": true, + "nullable": false + } + } + }, + { + "column": { + "name": "name", + "type": "varchar(50)" + } + }, + { + "column": { + "name": "addresss1", + "type": "varchar(50)" + } + }, + { + "column": { + "name": "addresss2", + "type": "varchar(50)" + } + }, + { + "column": { + "name": "city", + "type": "varchar(30)" + } + } + ] + } } - ] - }, - "changeset": { - "id": "2", - "author": "your.name", - "changes": [ + ] + }, + "changeSet": { + "id": "2", + "author": "your.name", + "changes": [ { - "createTable": - { - "tablename": "company", - "columns": [ - { - "column": { - "name": "id", - "type": "int", - "autoincrement": true, - "constraints": { - "primarykey": true, - "nullable": false - } - } - }, - { - "column": { - "name": "name", - "type": "varchar(50)" - } - }, - { - "column": { - "name": "addresss1", - "type": "varchar(50)" - } - }, - { - "column": { - "name": "addresss2", - "type": "varchar(50)" - } - }, - { - "column": { - "name": "city", - "type": "varchar(30)" - } - } - ] - } + "createTable": { + "tablename": "company", + "columns": [ + { + "column": { + "name": "id", + "type": "int", + "autoincrement": true, + "constraints": { + "primarykey": true, + "nullable": false + } + } + }, + { + "column": { + "name": "name", + "type": "varchar(50)" + } + }, + { + "column": { + "name": "addresss1", + "type": "varchar(50)" + } + }, + { + "column": { + "name": "addresss2", + "type": "varchar(50)" + } + }, + { + "column": { + "name": "city", + "type": "varchar(30)" + } + } + ] + } } - ] - }, - "changeset": { - "id": "3", - "author": "your.name", - "changes": [ + ] + }, + "changeSet": { + "id": "3", + "author": "your.name", + "changes": [ { - "addColumn": - { - "tablename": "company", - "columns": [ - { - "column": { - "name": "country", - "type": "varchar(2)" - } - } - ] - } + "addColumn": { + "tableName": "company", + "columns": [ + { + "column": { + "name": "country", + "type": "varchar(2)" + } + } + ] + } } - ] + ] + } } ]} From b55b75668fbc7c18c95f3f2555e9a2d81387ad5a Mon Sep 17 00:00:00 2001 From: Wesley Willard Date: Mon, 13 Dec 2021 10:36:38 -0600 Subject: [PATCH 10/37] Another sample changelog modification DAT-8601 --- .../examples/json/example-changelog.json | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/liquibase-core/src/main/resources/liquibase/examples/json/example-changelog.json b/liquibase-core/src/main/resources/liquibase/examples/json/example-changelog.json index de1539e031d..e502227fca6 100644 --- a/liquibase-core/src/main/resources/liquibase/examples/json/example-changelog.json +++ b/liquibase-core/src/main/resources/liquibase/examples/json/example-changelog.json @@ -6,13 +6,13 @@ "changes": [ { "createTable": { - "tablename": "person", + "tableName": "person", "columns": [ { "column": { "name": "id", "type": "int", - "autoincrement": true, + "autoIncrement": true, "constraints": { "primarykey": true, "nullable": false @@ -45,22 +45,23 @@ } ] } - } - ] - }, + }] + } + }, + { "changeSet": { "id": "2", "author": "your.name", "changes": [ { "createTable": { - "tablename": "company", + "tableName": "company", "columns": [ { "column": { "name": "id", "type": "int", - "autoincrement": true, + "autoIncrement": true, "constraints": { "primarykey": true, "nullable": false @@ -93,9 +94,10 @@ } ] } - } - ] - }, + }] + } + }, + { "changeSet": { "id": "3", "author": "your.name", @@ -112,8 +114,7 @@ } ] } - } - ] + }] } } ]} From 6b406df79773b53fc3c0d8e685bc362b2b145541 Mon Sep 17 00:00:00 2001 From: Steven Massaro Date: Tue, 14 Dec 2021 09:58:42 -0600 Subject: [PATCH 11/37] Revert "Move interactive prompting logic into core module (DAT-8642) (#2255)" This reverts commit 2c70056777f6753a1d685f663da31dbb91eb1c11. --- .../AbstractCommandLineValueGetter.java | 136 ------------------ .../ui/interactive/DynamicRuleParameter.java | 65 --------- .../IInteractivelyPromptableEnum.java | 29 ---- ...ractivePromptableCustomizationWrapper.java | 69 --------- 4 files changed, 299 deletions(-) delete mode 100644 liquibase-core/src/main/java/liquibase/ui/interactive/AbstractCommandLineValueGetter.java delete mode 100644 liquibase-core/src/main/java/liquibase/ui/interactive/DynamicRuleParameter.java delete mode 100644 liquibase-core/src/main/java/liquibase/ui/interactive/IInteractivelyPromptableEnum.java delete mode 100644 liquibase-core/src/main/java/liquibase/ui/interactive/InteractivePromptableCustomizationWrapper.java diff --git a/liquibase-core/src/main/java/liquibase/ui/interactive/AbstractCommandLineValueGetter.java b/liquibase-core/src/main/java/liquibase/ui/interactive/AbstractCommandLineValueGetter.java deleted file mode 100644 index 864add7e9b9..00000000000 --- a/liquibase-core/src/main/java/liquibase/ui/interactive/AbstractCommandLineValueGetter.java +++ /dev/null @@ -1,136 +0,0 @@ -package liquibase.ui.interactive; - -import liquibase.Scope; -import liquibase.ui.InputHandler; - -import java.util.List; - -/** - * This class represents the basis for prompts to the user to input values, and should be used as part of the process - * of obtaining user input for quality checks. - * @param the type the user is expected to enter - */ -public abstract class AbstractCommandLineValueGetter { - - /** - * The type of the value that will be obtained. - */ - private final Class clazz; - - /** - * Create a new value getter. - * @param clazz the type of the value that will be obtained - */ - public AbstractCommandLineValueGetter(Class clazz) { - this.clazz = clazz; - } - - /** - * Prompt the user to enter a value for a parameter, and include the existing value in the prompt. - * @param parameter the parameter to obtain a value for - * @param newParameterValues a list of the values that have already been entered by the user during this interactive CLI parameter prompting situation - * @return the value entered by the user - */ - public final T prompt(InteractivePromptableCustomizationWrapper parameter, List newParameterValues, Object currentValue) { - // determine which value should be displayed in square brackets as the value that will be selected if user presses enter - Object valueToPromptAsDefault; - if (currentValue != null) { - /* - * The value is being converted here, because enum types get read from the check settings file as strings. - * As a result, the default value and current value (which should be the same type), are not; the default - * value would be an enum and the current value would be a string. - */ - try { - valueToPromptAsDefault = convert((String) currentValue); - } catch (Exception e) { - valueToPromptAsDefault = currentValue; - } - } else { - valueToPromptAsDefault = parameter.getDefaultValue(); - } - - // assume the user just hit enter to accept the default and revalidate it, since hitting enter to accept the - // default skips the validation logic in ConsoleUIService. Reprompt endlessly until a valid value is entered. - T prompt = null; - boolean valid = false; - while (!valid && (prompt == null || prompt.equals(valueToPromptAsDefault))) { - prompt = doPrompt(parameter, newParameterValues, valueToPromptAsDefault, (currentValue != null || parameter.getDefaultValue() != null)); - - try { - valid = doValidate(parameter, newParameterValues, prompt); - } catch (IllegalArgumentException e) { - Scope.getCurrentScope().getUI().sendErrorMessage("Invalid value: '" + prompt + "': " + e.getMessage()); - } - } - return prompt; - } - - private T doPrompt(InteractivePromptableCustomizationWrapper parameter, List newParameterValues, Object valueToPromptAsDefault, boolean shouldAllowEmptyValues) { - return Scope.getCurrentScope().getUI().prompt(getMessage(parameter), (T) valueToPromptAsDefault, new InputHandler() { - @Override - public T parseInput(String input, Class type) throws IllegalArgumentException { - T convert; - try { - convert = AbstractCommandLineValueGetter.this.convert(input); - } catch (Exception e) { - if (e.getMessage() != null) { - throw new IllegalArgumentException( - String.format("Invalid value: '%s': %s", input, e.getMessage()), e); - } - throw new IllegalArgumentException(e); - } - - try { - if (!doValidate(parameter, newParameterValues, convert)) { - throw new IllegalArgumentException("The supplied value is not valid."); - } - } catch (Exception e) { - if (e.getMessage() != null) { - throw new IllegalArgumentException( - String.format("Invalid value: '%s': %s", input, e.getMessage()), e); - } - throw new IllegalArgumentException( - String.format("Invalid value: '%s': The supplied value is not valid.", input), e); - } - return convert; - } - - @Override - public boolean shouldAllowEmptyInput() { - // We do not allow empty input, because as of right now, all parameters require values. In the future, - // there may be parameters which permit empty values, which would need to tie in here. - return shouldAllowEmptyValues; - } - }, clazz); - } - - private boolean doValidate(InteractivePromptableCustomizationWrapper parameter, List newParameterValues, T convert) throws IllegalArgumentException { - if (parameter.getValidationCallbackOverride() != null) { - return parameter.getValidationCallbackOverride().apply(convert, newParameterValues); - } else { - return AbstractCommandLineValueGetter.this.validate(convert); - } - } - - /** - * Generate the prompt message. - * @param parameter the parameter to prompt for - * @return the message - */ - private String getMessage(InteractivePromptableCustomizationWrapper parameter) { - return parameter.getParameter().getUiMessage() + " (options: " + parameter.getParameter().getOptions() + ")"; - } - - /** - * Given the input from the user, after being converted, validate it. - * @return true if it is valid - */ - public abstract boolean validate(T input); - - /** - * Given the raw input string from the user, convert it to the right type. - * @param input the raw input string from the prompt - * @return the converted input - */ - public abstract T convert(String input); -} diff --git a/liquibase-core/src/main/java/liquibase/ui/interactive/DynamicRuleParameter.java b/liquibase-core/src/main/java/liquibase/ui/interactive/DynamicRuleParameter.java deleted file mode 100644 index 16023baf104..00000000000 --- a/liquibase-core/src/main/java/liquibase/ui/interactive/DynamicRuleParameter.java +++ /dev/null @@ -1,65 +0,0 @@ -package liquibase.ui.interactive; - - -import java.util.Objects; - - -public class DynamicRuleParameter { - - /** - * The actual underlying DynamicRuleParameterEnum that corresponds to this value. We store the parameter as a string, - * because different versions of Liquibase might not contain the enum that the rule requires. We want to be able to - * parse newer conf files with older versions of Liquibase, without a SnakeYaml error that it cannot find the enum - * value. - */ - private String parameter; - private Object value; - - /** - * Constructor for SnakeYaml - */ - public DynamicRuleParameter() { - } - - public DynamicRuleParameter(IInteractivelyPromptableEnum parameter, Object value) { - Objects.requireNonNull(parameter); - this.parameter = parameter.toString(); - this.value = value; - } - - public DynamicRuleParameter(String parameter, Object value) { - Objects.requireNonNull(parameter); - this.parameter = parameter; - this.value = value; - } - - public String getParameter() { - return parameter; - } - - public void setParameter(String parameter) { - this.parameter = parameter; - } - - public Object getValue() { - return value; - } - - public void setValue(Object value) { - this.value = value; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - DynamicRuleParameter that = (DynamicRuleParameter) o; - return Objects.equals(parameter, that.parameter) && value.equals(that.value); - } - - @Override - public int hashCode() { - return Objects.hash(parameter, value); - } -} - diff --git a/liquibase-core/src/main/java/liquibase/ui/interactive/IInteractivelyPromptableEnum.java b/liquibase-core/src/main/java/liquibase/ui/interactive/IInteractivelyPromptableEnum.java deleted file mode 100644 index ed76c26679b..00000000000 --- a/liquibase-core/src/main/java/liquibase/ui/interactive/IInteractivelyPromptableEnum.java +++ /dev/null @@ -1,29 +0,0 @@ -package liquibase.ui.interactive; - - -public interface IInteractivelyPromptableEnum { - /** - * Description of the parameter to be used in the UI output. - */ - String getDescription(); - - /** - * The default value of the parameter if the parameter has not been customized. - */ - Object getDefaultValue(); - - /** - * The implementation of the value getter that will be used to prompt the user to input a value. - */ - AbstractCommandLineValueGetter getInteractiveCommandLineValueGetter(); - - /** - * Parameter message to be used in the UI output. - */ - String getUiMessage(); - - /* - * Possible options for this parameter - */ - String getOptions(); -} diff --git a/liquibase-core/src/main/java/liquibase/ui/interactive/InteractivePromptableCustomizationWrapper.java b/liquibase-core/src/main/java/liquibase/ui/interactive/InteractivePromptableCustomizationWrapper.java deleted file mode 100644 index be0659ae562..00000000000 --- a/liquibase-core/src/main/java/liquibase/ui/interactive/InteractivePromptableCustomizationWrapper.java +++ /dev/null @@ -1,69 +0,0 @@ -package liquibase.ui.interactive; - - -import java.util.List; -import java.util.function.BiFunction; -import java.util.function.Function; - -/** - * This class exists to wrap the parameter enum so that individual usages can provide specific - * overrides of the logic defined in the enum class. You should access the underlying parameter as little as possible - * and instead, access it using getter wrapper methods in this class. That will allow future developers to specify - * custom overrides for each rule (for example, overriding the default value for a specific usage without - * affecting all others). - */ -public class InteractivePromptableCustomizationWrapper { - private final IInteractivelyPromptableEnum parameter; - private final BiFunction, Boolean> validationCallbackOverride; - private final Function, Boolean> shouldPrompt; - - public InteractivePromptableCustomizationWrapper(IInteractivelyPromptableEnum parameter) { - this.parameter = parameter; - this.validationCallbackOverride = null; - this.shouldPrompt = null; - } - - public InteractivePromptableCustomizationWrapper(IInteractivelyPromptableEnum parameter, BiFunction, Boolean> validationCallbackOverride, Function, Boolean> shouldPrompt) { - this.parameter = parameter; - this.validationCallbackOverride = validationCallbackOverride; - this.shouldPrompt = shouldPrompt; - } - - public Object getDefaultValue() { - return parameter.getDefaultValue(); - } - - public IInteractivelyPromptableEnum getParameter() { - return parameter; - } - - public AbstractCommandLineValueGetter getInteractiveCommandLineValueGetter() { - return parameter.getInteractiveCommandLineValueGetter(); - } - - public BiFunction, Boolean> getValidationCallbackOverride() { - return validationCallbackOverride; - } - - public Function, Boolean> getShouldPrompt() { - return shouldPrompt; - } - - @Override - public String toString() { - return getParameter().toString(); - } - - /** - * Determine if the rule parameter should be prompted for in a checks customize scenario. - * @param existingNewValues the values already provided in the checks customize session - * @return true if the prompt should occur for this parameter, false if not - */ - public boolean shouldPrompt(List existingNewValues) { - boolean resp = true; - if (shouldPrompt != null) { - resp = shouldPrompt.apply(existingNewValues); - } - return resp; - } -} From ce9e19af23cdf9c66c085ba647bc3fb82ea3e714 Mon Sep 17 00:00:00 2001 From: Wesley Willard Date: Tue, 14 Dec 2021 22:26:50 -0600 Subject: [PATCH 12/37] Modified yaml sample changelog Test fix DAT-8724 --- .../resources/liquibase/examples/yaml/example-changelog.yaml | 2 +- .../extension/testing/setup/SetupCleanResources.groovy | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/liquibase-core/src/main/resources/liquibase/examples/yaml/example-changelog.yaml b/liquibase-core/src/main/resources/liquibase/examples/yaml/example-changelog.yaml index 1334cb9795c..e102c5b95dc 100644 --- a/liquibase-core/src/main/resources/liquibase/examples/yaml/example-changelog.yaml +++ b/liquibase-core/src/main/resources/liquibase/examples/yaml/example-changelog.yaml @@ -43,5 +43,5 @@ databaseChangeLog: tableName: company columns: - column: - name: id + name: country type: varchar(2) diff --git a/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/setup/SetupCleanResources.groovy b/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/setup/SetupCleanResources.groovy index beddeb51ef7..20db33ac81b 100644 --- a/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/setup/SetupCleanResources.groovy +++ b/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/setup/SetupCleanResources.groovy @@ -48,6 +48,9 @@ class SetupCleanResources extends TestSetup { f = new File(url.toURI()) } + if (! f.exists()) { + continue + } if (f.isFile()) { f.delete() } else { From 5fa3257137d9cac7802867294ce73c73c504907f Mon Sep 17 00:00:00 2001 From: Steven Massaro Date: Wed, 15 Dec 2021 08:12:36 -0600 Subject: [PATCH 13/37] when reprompting for input after an invalid value, show the default value in brackets --- liquibase-core/src/main/java/liquibase/ui/ConsoleUIService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/liquibase-core/src/main/java/liquibase/ui/ConsoleUIService.java b/liquibase-core/src/main/java/liquibase/ui/ConsoleUIService.java index 5e29c6c4ff5..ef4b7637c63 100644 --- a/liquibase-core/src/main/java/liquibase/ui/ConsoleUIService.java +++ b/liquibase-core/src/main/java/liquibase/ui/ConsoleUIService.java @@ -113,7 +113,7 @@ public T prompt(String prompt, T valueIfNoEntry, InputHandler inputHandle message = "Invalid value: \"" + input + "\""; } this.sendMessage(message); - this.sendMessage(prompt + ": "); + this.sendMessage(initialMessage + ": "); } } } From 70e11e78880861ecf985f411088fe766366a0b4c Mon Sep 17 00:00:00 2001 From: Steven Massaro Date: Wed, 15 Dec 2021 08:14:25 -0600 Subject: [PATCH 14/37] interactively prompt for input from the init project command --- .../commandline/LiquibaseCommandLine.java | 3 +- .../java/liquibase/command/CommandScope.java | 7 +++++ .../InteractivePromptingValueProvider.java | 30 +++++++++++++++++++ .../liquibase/ui/ConsoleUIServiceTest.groovy | 2 +- .../testing/command/CommandTests.groovy | 2 ++ 5 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 liquibase-core/src/main/java/liquibase/configuration/core/InteractivePromptingValueProvider.java diff --git a/liquibase-cli/src/main/java/liquibase/integration/commandline/LiquibaseCommandLine.java b/liquibase-cli/src/main/java/liquibase/integration/commandline/LiquibaseCommandLine.java index 05f62f98c51..c5c236c106f 100644 --- a/liquibase-cli/src/main/java/liquibase/integration/commandline/LiquibaseCommandLine.java +++ b/liquibase-cli/src/main/java/liquibase/integration/commandline/LiquibaseCommandLine.java @@ -8,6 +8,7 @@ import liquibase.configuration.ConfiguredValue; import liquibase.configuration.LiquibaseConfiguration; import liquibase.configuration.core.DefaultsFileValueProvider; +import liquibase.configuration.core.InteractivePromptingValueProvider; import liquibase.exception.CommandLineParsingException; import liquibase.exception.CommandValidationException; import liquibase.hub.HubConfiguration; @@ -467,7 +468,7 @@ public int getPrecedence() { } else { Scope.getCurrentScope().getLog(getClass()).fine("Cannot find local defaultsFile " + defaultsFile.getAbsolutePath()); } - + liquibaseConfiguration.registerProvider(new InteractivePromptingValueProvider()); return returnList; } diff --git a/liquibase-core/src/main/java/liquibase/command/CommandScope.java b/liquibase-core/src/main/java/liquibase/command/CommandScope.java index 04bd62f9881..ff6b1fef72b 100644 --- a/liquibase-core/src/main/java/liquibase/command/CommandScope.java +++ b/liquibase-core/src/main/java/liquibase/command/CommandScope.java @@ -58,6 +58,13 @@ public CommandDefinition getCommand() { return commandDefinition; } + /** + * Returns the complete config prefix (without a trailing period) for the command in this scope. + * @return + */ + public String getCompleteConfigPrefix() { + return completeConfigPrefix; + } /** * Adds the given key/value pair to the stored argument data. diff --git a/liquibase-core/src/main/java/liquibase/configuration/core/InteractivePromptingValueProvider.java b/liquibase-core/src/main/java/liquibase/configuration/core/InteractivePromptingValueProvider.java new file mode 100644 index 00000000000..05e942e4068 --- /dev/null +++ b/liquibase-core/src/main/java/liquibase/configuration/core/InteractivePromptingValueProvider.java @@ -0,0 +1,30 @@ +package liquibase.configuration.core; + +import liquibase.configuration.AbstractMapConfigurationValueProvider; + +import java.util.Map; +import java.util.SortedMap; +import java.util.TreeMap; + +/** + * This value provider handles values obtained during interactive CLI prompting. + */ +public class InteractivePromptingValueProvider extends AbstractMapConfigurationValueProvider { + + public static final SortedMap values = new TreeMap<>(); + + @Override + protected Map getMap() { + return values; + } + + @Override + protected String getSourceDescription() { + return "CLI interactive prompts"; + } + + @Override + public int getPrecedence() { + return 500; + } +} \ No newline at end of file diff --git a/liquibase-core/src/test/groovy/liquibase/ui/ConsoleUIServiceTest.groovy b/liquibase-core/src/test/groovy/liquibase/ui/ConsoleUIServiceTest.groovy index ce4702e439c..8c8001c78e8 100644 --- a/liquibase-core/src/test/groovy/liquibase/ui/ConsoleUIServiceTest.groovy +++ b/liquibase-core/src/test/groovy/liquibase/ui/ConsoleUIServiceTest.groovy @@ -29,7 +29,7 @@ class ConsoleUIServiceTest extends Specification { "" | null | null | "Prompt here: " | String "x" | null | "x" | "Prompt here: " | String "1234" | null | 1234 | "Prompt here: " | Integer - ["x", "1234"] as String[] | 0 | 1234 | "Prompt here [0]: \nInvalid value: 'x': For input string: \"x\"\nPrompt here: " | Integer + ["x", "1234"] as String[] | 0 | 1234 | "Prompt here [0]: \nInvalid value: 'x': For input string: \"x\"\nPrompt here [0]:" | Integer "true" | false | true | "Prompt here [false]: " | Boolean "false" | false | false | "Prompt here [false]: " | Boolean } diff --git a/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/command/CommandTests.groovy b/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/command/CommandTests.groovy index b61aad4ffdc..5c18de48cde 100644 --- a/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/command/CommandTests.groovy +++ b/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/command/CommandTests.groovy @@ -15,6 +15,7 @@ import liquibase.command.core.InternalSnapshotCommandStep import liquibase.configuration.AbstractMapConfigurationValueProvider import liquibase.configuration.ConfigurationValueProvider import liquibase.configuration.LiquibaseConfiguration +import liquibase.configuration.core.InteractivePromptingValueProvider import liquibase.database.Database import liquibase.database.DatabaseFactory import liquibase.database.jvm.JdbcConnection @@ -86,6 +87,7 @@ class CommandTests extends Specification { } Scope.currentScope.getSingleton(LiquibaseConfiguration).registerProvider(propertiesProvider) + Scope.currentScope.getSingleton(LiquibaseConfiguration).registerProvider(new InteractivePromptingValueProvider()) } def cleanup() { From e8ed4070a24ad608397ea1a98e42d4216e96a754 Mon Sep 17 00:00:00 2001 From: Wesley Willard Date: Wed, 15 Dec 2021 09:32:08 -0600 Subject: [PATCH 15/37] Renamed example files DAT-8724 --- .../examples/json/{blank.changelog.json => blank-changelog.json} | 0 .../json/{example-changeset.json => example-changeset-json.txt} | 0 .../examples/sql/{blank.changelog.sql => blank-changelog.sql} | 0 .../sql/{example-changeset.sql => example-changeset-sql.txt} | 0 .../examples/xml/{blank.changelog.xml => blank-changelog.xml} | 0 .../xml/{example-changeset.xml => example-changeset-xml.txt} | 0 .../examples/yaml/{blank.changelog.yaml => blank-changelog.yaml} | 0 .../yaml/{example-changeset.yaml => example-changeset-yaml.txt} | 0 8 files changed, 0 insertions(+), 0 deletions(-) rename liquibase-core/src/main/resources/liquibase/examples/json/{blank.changelog.json => blank-changelog.json} (100%) rename liquibase-core/src/main/resources/liquibase/examples/json/{example-changeset.json => example-changeset-json.txt} (100%) rename liquibase-core/src/main/resources/liquibase/examples/sql/{blank.changelog.sql => blank-changelog.sql} (100%) rename liquibase-core/src/main/resources/liquibase/examples/sql/{example-changeset.sql => example-changeset-sql.txt} (100%) rename liquibase-core/src/main/resources/liquibase/examples/xml/{blank.changelog.xml => blank-changelog.xml} (100%) rename liquibase-core/src/main/resources/liquibase/examples/xml/{example-changeset.xml => example-changeset-xml.txt} (100%) rename liquibase-core/src/main/resources/liquibase/examples/yaml/{blank.changelog.yaml => blank-changelog.yaml} (100%) rename liquibase-core/src/main/resources/liquibase/examples/yaml/{example-changeset.yaml => example-changeset-yaml.txt} (100%) diff --git a/liquibase-core/src/main/resources/liquibase/examples/json/blank.changelog.json b/liquibase-core/src/main/resources/liquibase/examples/json/blank-changelog.json similarity index 100% rename from liquibase-core/src/main/resources/liquibase/examples/json/blank.changelog.json rename to liquibase-core/src/main/resources/liquibase/examples/json/blank-changelog.json diff --git a/liquibase-core/src/main/resources/liquibase/examples/json/example-changeset.json b/liquibase-core/src/main/resources/liquibase/examples/json/example-changeset-json.txt similarity index 100% rename from liquibase-core/src/main/resources/liquibase/examples/json/example-changeset.json rename to liquibase-core/src/main/resources/liquibase/examples/json/example-changeset-json.txt diff --git a/liquibase-core/src/main/resources/liquibase/examples/sql/blank.changelog.sql b/liquibase-core/src/main/resources/liquibase/examples/sql/blank-changelog.sql similarity index 100% rename from liquibase-core/src/main/resources/liquibase/examples/sql/blank.changelog.sql rename to liquibase-core/src/main/resources/liquibase/examples/sql/blank-changelog.sql diff --git a/liquibase-core/src/main/resources/liquibase/examples/sql/example-changeset.sql b/liquibase-core/src/main/resources/liquibase/examples/sql/example-changeset-sql.txt similarity index 100% rename from liquibase-core/src/main/resources/liquibase/examples/sql/example-changeset.sql rename to liquibase-core/src/main/resources/liquibase/examples/sql/example-changeset-sql.txt diff --git a/liquibase-core/src/main/resources/liquibase/examples/xml/blank.changelog.xml b/liquibase-core/src/main/resources/liquibase/examples/xml/blank-changelog.xml similarity index 100% rename from liquibase-core/src/main/resources/liquibase/examples/xml/blank.changelog.xml rename to liquibase-core/src/main/resources/liquibase/examples/xml/blank-changelog.xml diff --git a/liquibase-core/src/main/resources/liquibase/examples/xml/example-changeset.xml b/liquibase-core/src/main/resources/liquibase/examples/xml/example-changeset-xml.txt similarity index 100% rename from liquibase-core/src/main/resources/liquibase/examples/xml/example-changeset.xml rename to liquibase-core/src/main/resources/liquibase/examples/xml/example-changeset-xml.txt diff --git a/liquibase-core/src/main/resources/liquibase/examples/yaml/blank.changelog.yaml b/liquibase-core/src/main/resources/liquibase/examples/yaml/blank-changelog.yaml similarity index 100% rename from liquibase-core/src/main/resources/liquibase/examples/yaml/blank.changelog.yaml rename to liquibase-core/src/main/resources/liquibase/examples/yaml/blank-changelog.yaml diff --git a/liquibase-core/src/main/resources/liquibase/examples/yaml/example-changeset.yaml b/liquibase-core/src/main/resources/liquibase/examples/yaml/example-changeset-yaml.txt similarity index 100% rename from liquibase-core/src/main/resources/liquibase/examples/yaml/example-changeset.yaml rename to liquibase-core/src/main/resources/liquibase/examples/yaml/example-changeset-yaml.txt From d985c5371a4beb3960cf06a382d6eed2597e942f Mon Sep 17 00:00:00 2001 From: Wesley Willard Date: Wed, 15 Dec 2021 14:54:41 -0600 Subject: [PATCH 16/37] Fix doc issues in files DAT-8724 --- .../liquibase/examples/sql/example-changeset-sql.txt | 2 +- .../resources/liquibase/examples/yaml/blank-changelog.yaml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/liquibase-core/src/main/resources/liquibase/examples/sql/example-changeset-sql.txt b/liquibase-core/src/main/resources/liquibase/examples/sql/example-changeset-sql.txt index 72fd788ec8e..88f181639b6 100644 --- a/liquibase-core/src/main/resources/liquibase/examples/sql/example-changeset-sql.txt +++ b/liquibase-core/src/main/resources/liquibase/examples/sql/example-changeset-sql.txt @@ -1,4 +1,4 @@ -(example-changeset.sql) +(example-changeset-sql.txt) --changeset dev:1 create table test1( diff --git a/liquibase-core/src/main/resources/liquibase/examples/yaml/blank-changelog.yaml b/liquibase-core/src/main/resources/liquibase/examples/yaml/blank-changelog.yaml index 2a45be37f4c..771e1911fde 100644 --- a/liquibase-core/src/main/resources/liquibase/examples/yaml/blank-changelog.yaml +++ b/liquibase-core/src/main/resources/liquibase/examples/yaml/blank-changelog.yaml @@ -3,10 +3,10 @@ databaseChangeLog: id: 1 author: your.name changes: - # Insert Yaml change objects here https://www.liquibase.org/documentation/xml_format.html + # Insert Yaml change objects here https://www.liquibase.org/documentation/yaml_format.html - changeSet: id: 2 author: your.name changes: - # Insert Yaml change objects here https://www.liquibase.org/documentation/xml_format.html + # Insert Yaml change objects here https://www.liquibase.org/documentation/yaml_format.html From 82581561c9c7424bad791782093b815e7f37d402 Mon Sep 17 00:00:00 2001 From: Steven Massaro Date: Mon, 20 Dec 2021 10:32:35 -0600 Subject: [PATCH 17/37] Fix prompt bug and add "run after method" logic to command tests (DAT-8804) (#2276) Add "run after method" callable to CommandTests.groovy Fix a bug where reprompting for user input after receiving invalid input does not display the default value in brackets --- .../src/main/java/liquibase/command/CommandScope.java | 7 +++++++ .../src/main/java/liquibase/ui/ConsoleUIService.java | 2 +- .../groovy/liquibase/ui/ConsoleUIServiceTest.groovy | 2 +- .../extension/testing/command/CommandTests.groovy | 11 +++++++++++ 4 files changed, 20 insertions(+), 2 deletions(-) diff --git a/liquibase-core/src/main/java/liquibase/command/CommandScope.java b/liquibase-core/src/main/java/liquibase/command/CommandScope.java index 04bd62f9881..ff6b1fef72b 100644 --- a/liquibase-core/src/main/java/liquibase/command/CommandScope.java +++ b/liquibase-core/src/main/java/liquibase/command/CommandScope.java @@ -58,6 +58,13 @@ public CommandDefinition getCommand() { return commandDefinition; } + /** + * Returns the complete config prefix (without a trailing period) for the command in this scope. + * @return + */ + public String getCompleteConfigPrefix() { + return completeConfigPrefix; + } /** * Adds the given key/value pair to the stored argument data. diff --git a/liquibase-core/src/main/java/liquibase/ui/ConsoleUIService.java b/liquibase-core/src/main/java/liquibase/ui/ConsoleUIService.java index 5e29c6c4ff5..ef4b7637c63 100644 --- a/liquibase-core/src/main/java/liquibase/ui/ConsoleUIService.java +++ b/liquibase-core/src/main/java/liquibase/ui/ConsoleUIService.java @@ -113,7 +113,7 @@ public T prompt(String prompt, T valueIfNoEntry, InputHandler inputHandle message = "Invalid value: \"" + input + "\""; } this.sendMessage(message); - this.sendMessage(prompt + ": "); + this.sendMessage(initialMessage + ": "); } } } diff --git a/liquibase-core/src/test/groovy/liquibase/ui/ConsoleUIServiceTest.groovy b/liquibase-core/src/test/groovy/liquibase/ui/ConsoleUIServiceTest.groovy index ce4702e439c..8c8001c78e8 100644 --- a/liquibase-core/src/test/groovy/liquibase/ui/ConsoleUIServiceTest.groovy +++ b/liquibase-core/src/test/groovy/liquibase/ui/ConsoleUIServiceTest.groovy @@ -29,7 +29,7 @@ class ConsoleUIServiceTest extends Specification { "" | null | null | "Prompt here: " | String "x" | null | "x" | "Prompt here: " | String "1234" | null | 1234 | "Prompt here: " | Integer - ["x", "1234"] as String[] | 0 | 1234 | "Prompt here [0]: \nInvalid value: 'x': For input string: \"x\"\nPrompt here: " | Integer + ["x", "1234"] as String[] | 0 | 1234 | "Prompt here [0]: \nInvalid value: 'x': For input string: \"x\"\nPrompt here [0]:" | Integer "true" | false | true | "Prompt here [false]: " | Boolean "false" | false | false | "Prompt here [false]: " | Boolean } diff --git a/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/command/CommandTests.groovy b/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/command/CommandTests.groovy index b61aad4ffdc..45aeadbbcc5 100644 --- a/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/command/CommandTests.groovy +++ b/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/command/CommandTests.groovy @@ -42,6 +42,7 @@ import org.junit.ComparisonFailure import spock.lang.Specification import spock.lang.Unroll +import java.util.concurrent.Callable import java.util.logging.Level import java.util.regex.Pattern @@ -290,6 +291,10 @@ Long Description: ${commandDefinition.getLongDescription() ?: "NOT SET"} } return } + } finally { + if (testDef.commandTestDefinition.afterMethodInvocation != null) { + testDef.commandTestDefinition.afterMethodInvocation.call() + } } } as Scope.ScopedRunnerWithReturn) @@ -552,6 +557,12 @@ Long Description: ${commandDefinition.getLongDescription() ?: "NOT SET"} List runTests = new ArrayList<>() String signature + /** + * An optional method that will be called after the execution of each run command. This is executed within + * the same scope as the command that is run for the test. This method will always be called, regardless of + * exceptions thrown from within the test. + */ + Callable afterMethodInvocation void run(@DelegatesTo(RunTestDefinition) Closure testClosure) { run(null, testClosure) From 0e4d091d0f3583ddb978675dded75a6305e973ab Mon Sep 17 00:00:00 2001 From: Wesley Willard Date: Wed, 22 Dec 2021 09:04:30 -0600 Subject: [PATCH 18/37] Added output capture to TestUI class DAT-8784 --- .../liquibase/extension/testing/command/CommandTests.groovy | 1 + 1 file changed, 1 insertion(+) diff --git a/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/command/CommandTests.groovy b/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/command/CommandTests.groovy index 924bc905eff..33cc999604a 100644 --- a/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/command/CommandTests.groovy +++ b/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/command/CommandTests.groovy @@ -1079,6 +1079,7 @@ Long Description: ${commandDefinition.getLongDescription() ?: "NOT SET"} @Override def T prompt(String prompt, T valueIfNoEntry, InputHandler inputHandler, Class type) { + this.sendMessage(prompt + ": "); return valueIfNoEntry } From 02472bb8c71ff257153f559fe0e4c22807800135 Mon Sep 17 00:00:00 2001 From: Wesley Willard Date: Thu, 23 Dec 2021 10:05:35 -0600 Subject: [PATCH 19/37] Remove unused file DAT-8784 --- .../InteractivePromptingValueProvider.java | 30 ------------------- 1 file changed, 30 deletions(-) delete mode 100644 liquibase-core/src/main/java/liquibase/configuration/core/InteractivePromptingValueProvider.java diff --git a/liquibase-core/src/main/java/liquibase/configuration/core/InteractivePromptingValueProvider.java b/liquibase-core/src/main/java/liquibase/configuration/core/InteractivePromptingValueProvider.java deleted file mode 100644 index 05e942e4068..00000000000 --- a/liquibase-core/src/main/java/liquibase/configuration/core/InteractivePromptingValueProvider.java +++ /dev/null @@ -1,30 +0,0 @@ -package liquibase.configuration.core; - -import liquibase.configuration.AbstractMapConfigurationValueProvider; - -import java.util.Map; -import java.util.SortedMap; -import java.util.TreeMap; - -/** - * This value provider handles values obtained during interactive CLI prompting. - */ -public class InteractivePromptingValueProvider extends AbstractMapConfigurationValueProvider { - - public static final SortedMap values = new TreeMap<>(); - - @Override - protected Map getMap() { - return values; - } - - @Override - protected String getSourceDescription() { - return "CLI interactive prompts"; - } - - @Override - public int getPrecedence() { - return 500; - } -} \ No newline at end of file From 8fb819cc03a5217a22b37095fb50bd4abe5ff735 Mon Sep 17 00:00:00 2001 From: Surya Aki Date: Thu, 23 Dec 2021 10:36:33 -0600 Subject: [PATCH 20/37] remove InteractivePromptingValueProvider --- .../liquibase/integration/commandline/LiquibaseCommandLine.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/liquibase-cli/src/main/java/liquibase/integration/commandline/LiquibaseCommandLine.java b/liquibase-cli/src/main/java/liquibase/integration/commandline/LiquibaseCommandLine.java index c5c236c106f..f5a1e3065a0 100644 --- a/liquibase-cli/src/main/java/liquibase/integration/commandline/LiquibaseCommandLine.java +++ b/liquibase-cli/src/main/java/liquibase/integration/commandline/LiquibaseCommandLine.java @@ -8,7 +8,6 @@ import liquibase.configuration.ConfiguredValue; import liquibase.configuration.LiquibaseConfiguration; import liquibase.configuration.core.DefaultsFileValueProvider; -import liquibase.configuration.core.InteractivePromptingValueProvider; import liquibase.exception.CommandLineParsingException; import liquibase.exception.CommandValidationException; import liquibase.hub.HubConfiguration; @@ -468,7 +467,6 @@ public int getPrecedence() { } else { Scope.getCurrentScope().getLog(getClass()).fine("Cannot find local defaultsFile " + defaultsFile.getAbsolutePath()); } - liquibaseConfiguration.registerProvider(new InteractivePromptingValueProvider()); return returnList; } From 17227e3b771308a769c54eb45897517a657794ea Mon Sep 17 00:00:00 2001 From: Surya Aki Date: Thu, 23 Dec 2021 10:38:07 -0600 Subject: [PATCH 21/37] remvoe InteractivePromptingValueProvider --- .../liquibase/extension/testing/command/CommandTests.groovy | 2 -- 1 file changed, 2 deletions(-) diff --git a/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/command/CommandTests.groovy b/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/command/CommandTests.groovy index 33cc999604a..b627b259338 100644 --- a/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/command/CommandTests.groovy +++ b/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/command/CommandTests.groovy @@ -15,7 +15,6 @@ import liquibase.command.core.InternalSnapshotCommandStep import liquibase.configuration.AbstractMapConfigurationValueProvider import liquibase.configuration.ConfigurationValueProvider import liquibase.configuration.LiquibaseConfiguration -import liquibase.configuration.core.InteractivePromptingValueProvider import liquibase.database.Database import liquibase.database.DatabaseFactory import liquibase.database.jvm.JdbcConnection @@ -88,7 +87,6 @@ class CommandTests extends Specification { } Scope.currentScope.getSingleton(LiquibaseConfiguration).registerProvider(propertiesProvider) - Scope.currentScope.getSingleton(LiquibaseConfiguration).registerProvider(new InteractivePromptingValueProvider()) } def cleanup() { From d7ebf6ec9d0886eb8b1d4822a7dc972b8ae2c0f0 Mon Sep 17 00:00:00 2001 From: Steven Massaro Date: Mon, 27 Dec 2021 08:20:23 -0600 Subject: [PATCH 22/37] ensure that the most recently created changelog file is prompted for --- .../extension/testing/command/CommandTests.groovy | 7 +++++++ .../testing/setup/SetupCreateTempResources.groovy | 9 +++++++++ 2 files changed, 16 insertions(+) diff --git a/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/command/CommandTests.groovy b/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/command/CommandTests.groovy index b627b259338..109bfe4bb57 100644 --- a/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/command/CommandTests.groovy +++ b/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/command/CommandTests.groovy @@ -860,6 +860,13 @@ Long Description: ${commandDefinition.getLongDescription() ?: "NOT SET"} this.setups.add(new SetupCreateTempResources(originalFile, newFile, baseDir)) } + /** + * @param fileLastModifiedDate if not null, the newly created file's last modified date will be set to this value + */ + void createTempResource(String originalFile, String newFile, String baseDir, Date fileLastModifiedDate) { + this.setups.add(new SetupCreateTempResources(originalFile, newFile, baseDir, fileLastModifiedDate)) + } + void createTempDirectoryResource(String directory) { this.setups.add(new SetupCreateDirectoryResources(directory)) } diff --git a/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/setup/SetupCreateTempResources.groovy b/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/setup/SetupCreateTempResources.groovy index 0de15a1aebd..f864ceaf770 100644 --- a/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/setup/SetupCreateTempResources.groovy +++ b/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/setup/SetupCreateTempResources.groovy @@ -8,15 +8,21 @@ class SetupCreateTempResources extends TestSetup { private String originalFile private String newFile private String baseDir + private Date lastModified SetupCreateTempResources(String originalFile, String newFile) { this(originalFile, newFile, "target/test-classes") } SetupCreateTempResources(String originalFile, String newFile, String baseDir) { + this(originalFile, newFile, baseDir, null) + } + + SetupCreateTempResources(String originalFile, String newFile, String baseDir, Date lastModified) { this.originalFile = originalFile this.newFile = newFile this.baseDir = baseDir + this.lastModified = lastModified } @Override @@ -26,5 +32,8 @@ class SetupCreateTempResources extends TestSetup { String contents = FileUtil.getContents(f) File outputFile = new File(baseDir, newFile) FileUtil.write(contents, outputFile) + if (lastModified != null) { + outputFile.setLastModified(lastModified.getTime()) + } } } From af8230c3a329a9cb20ec7d0755944d7c401295ed Mon Sep 17 00:00:00 2001 From: Wesley Willard Date: Mon, 3 Jan 2022 10:39:45 -0600 Subject: [PATCH 23/37] Use correct path to H2 jar in startup script DAT-8601 --- liquibase-core/src/main/resources/liquibase/examples/start-h2 | 2 +- .../src/main/resources/liquibase/examples/start-h2.bat | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/liquibase-core/src/main/resources/liquibase/examples/start-h2 b/liquibase-core/src/main/resources/liquibase/examples/start-h2 index 718090807bf..d10f0ee71c0 100644 --- a/liquibase-core/src/main/resources/liquibase/examples/start-h2 +++ b/liquibase-core/src/main/resources/liquibase/examples/start-h2 @@ -38,4 +38,4 @@ fi # echo "${JAVA_PATH}" -cp "${LIQUIBASE_HOME}/lib/h2-1.4.200.jar:${LIQUIBASE_HOME}/liquibase.jar" liquibase.example.StartH2Main -"${JAVA_PATH}" -cp "${LIQUIBASE_HOME}/lib/h2-1.4.200.jar:${LIQUIBASE_HOME}/liquibase.jar" liquibase.example.StartH2Main +"${JAVA_PATH}" -cp "${LIQUIBASE_HOME}/lib/h2-2.0.202.jar:${LIQUIBASE_HOME}/liquibase.jar" liquibase.example.StartH2Main diff --git a/liquibase-core/src/main/resources/liquibase/examples/start-h2.bat b/liquibase-core/src/main/resources/liquibase/examples/start-h2.bat index 21603ba57e4..41e8781eef5 100644 --- a/liquibase-core/src/main/resources/liquibase/examples/start-h2.bat +++ b/liquibase-core/src/main/resources/liquibase/examples/start-h2.bat @@ -31,4 +31,4 @@ if "%JAVA_HOME%"=="" ( set JAVA_PATH=%JAVA_HOME%\bin\java ) -"%JAVA_PATH%" -cp "%LIQUIBASE_HOME%\lib\h2-1.4.200.jar;%LIQUIBASE_HOME%\liquibase.jar" liquibase.example.StartH2Main +"%JAVA_PATH%" -cp "%LIQUIBASE_HOME%\lib\h2-2.0.202.jar;%LIQUIBASE_HOME%\liquibase.jar" liquibase.example.StartH2Main From 7865d448fd41466e50b5951b639ba031da67ed53 Mon Sep 17 00:00:00 2001 From: Steven Massaro Date: Mon, 3 Jan 2022 14:17:53 -0600 Subject: [PATCH 24/37] validate token with hub (DAT-8770) (#2313) --- .../main/java/liquibase/hub/HubService.java | 2 ++ .../java/liquibase/hub/HubServiceFactory.java | 5 +++++ .../liquibase/hub/core/MockHubService.java | 5 +++++ .../hub/core/StandardHubService.java | 5 +++++ .../main/java/liquibase/hub/model/ApiKey.java | 14 ++++++++++++ .../hub/model/CoreInitOnboardingResponse.java | 22 +++++++++++++++++++ 6 files changed, 53 insertions(+) create mode 100644 liquibase-core/src/main/java/liquibase/hub/model/ApiKey.java create mode 100644 liquibase-core/src/main/java/liquibase/hub/model/CoreInitOnboardingResponse.java diff --git a/liquibase-core/src/main/java/liquibase/hub/HubService.java b/liquibase-core/src/main/java/liquibase/hub/HubService.java index 82fc1aa8025..2505e8471ec 100644 --- a/liquibase-core/src/main/java/liquibase/hub/HubService.java +++ b/liquibase-core/src/main/java/liquibase/hub/HubService.java @@ -59,4 +59,6 @@ public interface HubService extends Plugin, PrioritizedService { void sendOperationChangeEvent(OperationChangeEvent operationChangeEvent) throws LiquibaseException; void sendOperationChanges(OperationChange operationChange) throws LiquibaseHubException; + + CoreInitOnboardingResponse validateOnboardingToken(String token) throws LiquibaseHubException; } \ No newline at end of file diff --git a/liquibase-core/src/main/java/liquibase/hub/HubServiceFactory.java b/liquibase-core/src/main/java/liquibase/hub/HubServiceFactory.java index 17b832bf509..079fb716e75 100644 --- a/liquibase-core/src/main/java/liquibase/hub/HubServiceFactory.java +++ b/liquibase-core/src/main/java/liquibase/hub/HubServiceFactory.java @@ -166,5 +166,10 @@ public void sendOperationChangeEvent(OperationChangeEvent operationChangeEvent) public void sendOperationChanges(OperationChange operationChange) throws LiquibaseHubException { } + + @Override + public CoreInitOnboardingResponse validateOnboardingToken(String token) throws LiquibaseHubException { + return null; + } } } diff --git a/liquibase-core/src/main/java/liquibase/hub/core/MockHubService.java b/liquibase-core/src/main/java/liquibase/hub/core/MockHubService.java index e21a5bef5f8..b691dd9dfa4 100644 --- a/liquibase-core/src/main/java/liquibase/hub/core/MockHubService.java +++ b/liquibase-core/src/main/java/liquibase/hub/core/MockHubService.java @@ -176,6 +176,11 @@ public String shortenLink(String url) throws LiquibaseException { return null; } + @Override + public CoreInitOnboardingResponse validateOnboardingToken(String token) throws LiquibaseHubException { + return null; + } + public void reset() { randomUUID = UUID.randomUUID(); diff --git a/liquibase-core/src/main/java/liquibase/hub/core/StandardHubService.java b/liquibase-core/src/main/java/liquibase/hub/core/StandardHubService.java index db78a0afde5..a15af6b4092 100644 --- a/liquibase-core/src/main/java/liquibase/hub/core/StandardHubService.java +++ b/liquibase-core/src/main/java/liquibase/hub/core/StandardHubService.java @@ -561,6 +561,11 @@ public void sendOperationChanges(OperationChange operationChange) throws Liquiba hubChangeList, ArrayList.class); } + @Override + public CoreInitOnboardingResponse validateOnboardingToken(String token) throws LiquibaseHubException { + return http.doPost("/api/v1/init", Collections.singletonMap("onboardingToken", token), CoreInitOnboardingResponse.class); + } + /** * Converts an object to a search string. * Any properties with non-null values are used as search arguments. diff --git a/liquibase-core/src/main/java/liquibase/hub/model/ApiKey.java b/liquibase-core/src/main/java/liquibase/hub/model/ApiKey.java new file mode 100644 index 00000000000..c6dbc977ed4 --- /dev/null +++ b/liquibase-core/src/main/java/liquibase/hub/model/ApiKey.java @@ -0,0 +1,14 @@ +package liquibase.hub.model; + +public class ApiKey { + + private String key; + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } +} diff --git a/liquibase-core/src/main/java/liquibase/hub/model/CoreInitOnboardingResponse.java b/liquibase-core/src/main/java/liquibase/hub/model/CoreInitOnboardingResponse.java new file mode 100644 index 00000000000..20679dbffdc --- /dev/null +++ b/liquibase-core/src/main/java/liquibase/hub/model/CoreInitOnboardingResponse.java @@ -0,0 +1,22 @@ +package liquibase.hub.model; + +public class CoreInitOnboardingResponse { + private ApiKey apiKey; + private Organization organization; + + public ApiKey getApiKey() { + return apiKey; + } + + public void setApiKey(ApiKey apiKey) { + this.apiKey = apiKey; + } + + public Organization getOrganization() { + return organization; + } + + public void setOrganization(Organization organization) { + this.organization = organization; + } +} From c4b330302bfcf4a9aa822d94e0aa972dd878bb27 Mon Sep 17 00:00:00 2001 From: Wesley willard Date: Tue, 4 Jan 2022 12:21:39 -0600 Subject: [PATCH 25/37] Initial implementation of init hub DAT-8769 (#2314) --- .../testing/setup/SetupCleanResources.groovy | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/setup/SetupCleanResources.groovy b/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/setup/SetupCleanResources.groovy index 20db33ac81b..ca9a068ee4c 100644 --- a/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/setup/SetupCleanResources.groovy +++ b/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/setup/SetupCleanResources.groovy @@ -1,16 +1,11 @@ package liquibase.extension.testing.setup - import liquibase.extension.testing.TestDatabaseConnections -import java.nio.file.FileSystems -import java.nio.file.Files -import java.nio.file.Path - class SetupCleanResources extends TestSetup { private final List resourcesToDelete = new ArrayList<>() - public enum CleanupMode { CLEAN_ON_SETUP, CLEAN_ON_CLEANUP, CLEAN_ON_BOTH} + enum CleanupMode { CLEAN_ON_SETUP, CLEAN_ON_CLEANUP, CLEAN_ON_BOTH} private CleanupMode cleanupMode SetupCleanResources(String[] resourcesToDelete) { @@ -38,9 +33,9 @@ class SetupCleanResources extends TestSetup { deleteFiles(resourcesToDelete) } - private void deleteFiles(List resourcesToDelete) { + private static void deleteFiles(List resourcesToDelete) { for (String fileToDelete : resourcesToDelete) { - File f = null + File f URL url = Thread.currentThread().getContextClassLoader().getResource(fileToDelete) if (url == null) { f = new File(fileToDelete) From ad37648f9f9abce0c93a4b907d99e6dace21706a Mon Sep 17 00:00:00 2001 From: Steven Massaro Date: Thu, 6 Jan 2022 08:46:05 -0600 Subject: [PATCH 26/37] update HubService to allow POSTing operation for an organization (DAT-8776) (#2336) --- .../main/java/liquibase/hub/HubService.java | 2 + .../java/liquibase/hub/HubServiceFactory.java | 5 +++ .../liquibase/hub/core/MockHubService.java | 5 +++ .../hub/core/StandardHubService.java | 40 ++++++++++++++----- 4 files changed, 42 insertions(+), 10 deletions(-) diff --git a/liquibase-core/src/main/java/liquibase/hub/HubService.java b/liquibase-core/src/main/java/liquibase/hub/HubService.java index 2505e8471ec..bca034a4f5b 100644 --- a/liquibase-core/src/main/java/liquibase/hub/HubService.java +++ b/liquibase-core/src/main/java/liquibase/hub/HubService.java @@ -45,6 +45,8 @@ public interface HubService extends Plugin, PrioritizedService { Operation createOperation(String operationType, String operationCommand, HubChangeLog changeLog, Connection connection) throws LiquibaseHubException; + Operation createOperationInOrganization(String operationType, String operationCommand, UUID organizationId) throws LiquibaseHubException; + OperationEvent sendOperationEvent(Operation operation, OperationEvent operationEvent) throws LiquibaseException; /** diff --git a/liquibase-core/src/main/java/liquibase/hub/HubServiceFactory.java b/liquibase-core/src/main/java/liquibase/hub/HubServiceFactory.java index 079fb716e75..1a1b34a9fe6 100644 --- a/liquibase-core/src/main/java/liquibase/hub/HubServiceFactory.java +++ b/liquibase-core/src/main/java/liquibase/hub/HubServiceFactory.java @@ -147,6 +147,11 @@ public Operation createOperation(String operationType, String operationCommand, return null; } + @Override + public Operation createOperationInOrganization(String operationType, String operationCommand, UUID organizationId) throws LiquibaseHubException { + return null; + } + @Override public OperationEvent sendOperationEvent(Operation operation, OperationEvent operationEvent) throws LiquibaseException { return null; diff --git a/liquibase-core/src/main/java/liquibase/hub/core/MockHubService.java b/liquibase-core/src/main/java/liquibase/hub/core/MockHubService.java index b691dd9dfa4..ab28cbc1433 100644 --- a/liquibase-core/src/main/java/liquibase/hub/core/MockHubService.java +++ b/liquibase-core/src/main/java/liquibase/hub/core/MockHubService.java @@ -156,6 +156,11 @@ public Operation createOperation(String operationType, String operationCommand, return null; } + @Override + public Operation createOperationInOrganization(String operationType, String operationCommand, UUID organizationId) throws LiquibaseHubException { + return null; + } + @Override public OperationEvent sendOperationEvent(Operation operation, OperationEvent operationEvent) throws LiquibaseException { return null; diff --git a/liquibase-core/src/main/java/liquibase/hub/core/StandardHubService.java b/liquibase-core/src/main/java/liquibase/hub/core/StandardHubService.java index a15af6b4092..227fc2b9264 100644 --- a/liquibase-core/src/main/java/liquibase/hub/core/StandardHubService.java +++ b/liquibase-core/src/main/java/liquibase/hub/core/StandardHubService.java @@ -402,7 +402,28 @@ public HubChangeLog getHubChangeLog(UUID changeLogId, String includeStatus) { @Override public Operation createOperation(String operationType, String operationCommand, HubChangeLog changeLog, Connection connection) throws LiquibaseHubException { + final IntegrationDetails integrationDetails = Scope.getCurrentScope().get("integrationDetails", IntegrationDetails.class); + Map requestBody = new HashMap<>(); + requestBody.put("connectionId", connection.getId()); + requestBody.put("connectionJdbcUrl", connection.getJdbcUrl()); + requestBody.put("projectId", connection.getProject() == null ? null : connection.getProject().getId()); + requestBody.put("changelogId", changeLog == null ? null : changeLog.getId()); + requestBody.put("operationType", operationType); + requestBody.put("operationCommand", operationCommand); + requestBody.put("operationStatusType", "PASS"); + requestBody.put("statusMessage", operationType); + requestBody.put("clientMetadata", getClientMetadata(integrationDetails)); + if (integrationDetails!=null) { + requestBody.put("operationParameters", getCleanOperationParameters(integrationDetails.getParameters())); + } + + final Operation operation = http.doPost("/api/v1/operations", requestBody, Operation.class); + operation.setConnection(connection); + return operation; + } + + private Map getClientMetadata(IntegrationDetails integrationDetails) { String hostName; try { hostName = InetAddress.getLocalHost().getHostName(); @@ -411,8 +432,6 @@ public Operation createOperation(String operationType, String operationCommand, hostName = null; } - final IntegrationDetails integrationDetails = Scope.getCurrentScope().get("integrationDetails", IntegrationDetails.class); - Map clientMetadata = new HashMap<>(); clientMetadata.put("liquibaseVersion", LiquibaseUtil.getBuildVersion()); clientMetadata.put("hostName", hostName); @@ -420,23 +439,24 @@ public Operation createOperation(String operationType, String operationCommand, if (integrationDetails != null) { clientMetadata.put("clientInterface", integrationDetails.getName()); } + return clientMetadata; + } + + @Override + public Operation createOperationInOrganization(String operationType, String operationCommand, UUID organizationId) throws LiquibaseHubException { + final IntegrationDetails integrationDetails = Scope.getCurrentScope().get("integrationDetails", IntegrationDetails.class); + Map requestBody = new HashMap<>(); - requestBody.put("connectionId", connection.getId()); - requestBody.put("connectionJdbcUrl", connection.getJdbcUrl()); - requestBody.put("projectId", connection.getProject() == null ? null : connection.getProject().getId()); - requestBody.put("changelogId", changeLog == null ? null : changeLog.getId()); requestBody.put("operationType", operationType); requestBody.put("operationCommand", operationCommand); requestBody.put("operationStatusType", "PASS"); requestBody.put("statusMessage", operationType); - requestBody.put("clientMetadata", clientMetadata); + requestBody.put("clientMetadata", getClientMetadata(integrationDetails)); if (integrationDetails!=null) { requestBody.put("operationParameters", getCleanOperationParameters(integrationDetails.getParameters())); } - final Operation operation = http.doPost("/api/v1/operations", requestBody, Operation.class); - operation.setConnection(connection); - return operation; + return http.doPost("/api/v1/organizations/" + organizationId.toString() + "/operations", requestBody, Operation.class); } protected Map getCleanOperationParameters(Map originalParams) { From 4b3b3a6b958aa0a739efe923bd301456fa49f37e Mon Sep 17 00:00:00 2001 From: Steven Massaro Date: Thu, 6 Jan 2022 09:27:49 -0600 Subject: [PATCH 27/37] modify mock hub service to support init hub tests (DAT-8973) (#2332) --- .../main/java/liquibase/hub/core/MockHubService.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/liquibase-core/src/main/java/liquibase/hub/core/MockHubService.java b/liquibase-core/src/main/java/liquibase/hub/core/MockHubService.java index ab28cbc1433..ab2f8f292d4 100644 --- a/liquibase-core/src/main/java/liquibase/hub/core/MockHubService.java +++ b/liquibase-core/src/main/java/liquibase/hub/core/MockHubService.java @@ -18,6 +18,8 @@ public class MockHubService implements HubService { public static UUID failUUID = UUID.randomUUID(); public static UUID notFoundChangeLogUUID = UUID.randomUUID(); public static Date operationCreateDate; + public static String apiKey = UUID.randomUUID().toString(); + public static UUID organizationId = UUID.randomUUID(); public List returnProjects = new ArrayList<>(); public List returnConnections; @@ -183,7 +185,15 @@ public String shortenLink(String url) throws LiquibaseException { @Override public CoreInitOnboardingResponse validateOnboardingToken(String token) throws LiquibaseHubException { - return null; + CoreInitOnboardingResponse response = new CoreInitOnboardingResponse(); + ApiKey ak = new ApiKey(); + ak.setKey(apiKey); + response.setApiKey(ak); + Organization organization = new Organization(); + organization.setId(organizationId); + organization.setName("neworg"); + response.setOrganization(organization); + return response; } public void reset() { From e93bfdb78aaf358a24d2630d7a1ca6cf2c57db78 Mon Sep 17 00:00:00 2001 From: Steven Massaro Date: Mon, 10 Jan 2022 10:01:50 -0600 Subject: [PATCH 28/37] refactoring of register changelog in support of init hub (DAT-8771) (#2343) --- .../core/RegisterChangelogCommandStep.java | 59 ++++++++++--------- .../liquibase/hub/core/MockHubService.java | 33 +++++++---- 2 files changed, 55 insertions(+), 37 deletions(-) diff --git a/liquibase-core/src/main/java/liquibase/command/core/RegisterChangelogCommandStep.java b/liquibase-core/src/main/java/liquibase/command/core/RegisterChangelogCommandStep.java index b36a47ccf13..ad0edbd1cbe 100644 --- a/liquibase-core/src/main/java/liquibase/command/core/RegisterChangelogCommandStep.java +++ b/liquibase-core/src/main/java/liquibase/command/core/RegisterChangelogCommandStep.java @@ -52,29 +52,32 @@ public String[][] defineCommandNames() { @Override public void run(CommandResultsBuilder resultsBuilder) throws Exception { - try (PrintWriter output = new PrintWriter(resultsBuilder.getOutputStream())) { + CommandScope commandScope = resultsBuilder.getCommandScope(); - final UIService ui = Scope.getCurrentScope().getUI(); - CommandScope commandScope = resultsBuilder.getCommandScope(); + // + // Access the HubService + // Stop if we do no have a key + // + final HubServiceFactory hubServiceFactory = Scope.getCurrentScope().getSingleton(HubServiceFactory.class); + if (!hubServiceFactory.isOnline()) { + throw new CommandExecutionException("The command registerChangeLog requires communication with Liquibase Hub, \nwhich is prevented by liquibase.hub.mode='off'. \nPlease set to 'all' or 'meta' and try again. \nLearn more at https://hub.liquibase.com"); + } - // - // Access the HubService - // Stop if we do no have a key - // - final HubServiceFactory hubServiceFactory = Scope.getCurrentScope().getSingleton(HubServiceFactory.class); - if (!hubServiceFactory.isOnline()) { - throw new CommandExecutionException("The command registerChangeLog requires communication with Liquibase Hub, \nwhich is prevented by liquibase.hub.mode='off'. \nPlease set to 'all' or 'meta' and try again. \nLearn more at https://hub.liquibase.com"); - } + // + // Check for existing changeLog file + // + String changeLogFile = commandScope.getArgumentValue(CHANGELOG_FILE_ARG); + UUID hubProjectId = commandScope.getArgumentValue(HUB_PROJECT_ID_ARG); + String hubProjectName = commandScope.getArgumentValue(HUB_PROJECT_NAME_ARG); - // - // Check for existing changeLog file - // - final HubService service = Scope.getCurrentScope().getSingleton(HubServiceFactory.class).getService(); - HubChangeLog hubChangeLog; - String changeLogFile = commandScope.getArgumentValue(CHANGELOG_FILE_ARG); - UUID hubProjectId = commandScope.getArgumentValue(HUB_PROJECT_ID_ARG); - String hubProjectName = commandScope.getArgumentValue(HUB_PROJECT_NAME_ARG); + doRegisterChangelog(changeLogFile, hubProjectId, hubProjectName, resultsBuilder, false); + } + public void doRegisterChangelog(String changeLogFile, UUID hubProjectId, String hubProjectName, CommandResultsBuilder resultsBuilder, boolean skipPromptIfOneProject) throws LiquibaseException, CommandLineParsingException { + try (PrintWriter output = new PrintWriter(resultsBuilder.getOutputStream())) { + + HubChangeLog hubChangeLog; + final HubService service = Scope.getCurrentScope().getSingleton(HubServiceFactory.class).getService(); // // CHeck for existing changeLog file // @@ -118,7 +121,7 @@ public void run(CommandResultsBuilder resultsBuilder) throws Exception { } output.print("\nProject '" + project.getName() + "' created with project ID '" + project.getId() + "'.\n\n"); } else { - project = retrieveOrCreateProject(service, commandScope); + project = retrieveOrCreateProject(service, changeLogFile, skipPromptIfOneProject); if (project == null) { throw new CommandExecutionException("Your changelog " + changeLogFile + " was not registered to any Liquibase Hub project. You can still run Liquibase commands, but no data will be saved in your Liquibase Hub account for monitoring or reports. Learn more at https://hub.liquibase.com."); } @@ -139,8 +142,8 @@ public void run(CommandResultsBuilder resultsBuilder) throws Exception { // Add the registered changelog ID to the results so that // the caller can use it // - ChangelogRewriter.ChangeLogRewriterResult changeLogRewriterResult = - ChangelogRewriter.addChangeLogId(changeLogFile, hubChangeLog.getId().toString(), databaseChangeLog); + ChangelogRewriter.ChangeLogRewriterResult changeLogRewriterResult = ChangelogRewriter.addChangeLogId(changeLogFile, hubChangeLog.getId().toString(), databaseChangeLog); + if (changeLogRewriterResult.success) { Scope.getCurrentScope().getLog(RegisterChangelogCommandStep.class).info(changeLogRewriterResult.message); output.println("* Changelog file '" + changeLogFile + "' with changelog ID '" + hubChangeLog.getId().toString() + "' has been " + @@ -151,16 +154,18 @@ public void run(CommandResultsBuilder resultsBuilder) throws Exception { } } - private Project retrieveOrCreateProject(HubService service, CommandScope commandScope) throws CommandLineParsingException, LiquibaseException, LiquibaseHubException { + private Project retrieveOrCreateProject(HubService service, String changeLogFile, boolean skipPromptIfOneProject) throws CommandLineParsingException, LiquibaseException, LiquibaseHubException { final UIService ui = Scope.getCurrentScope().getUI(); - String changeLogFile = commandScope.getArgumentValue(CHANGELOG_FILE_ARG); Project project = null; List projects = getProjectsFromHub(); + if (skipPromptIfOneProject && projects.size() == 1) { + return projects.get(0); + } boolean done = false; String input = null; while (!done) { - input = readProjectFromConsole(projects, commandScope); + input = readProjectFromConsole(projects, changeLogFile); try { if (input.equalsIgnoreCase("C")) { String projectName = readProjectNameFromConsole(); @@ -223,11 +228,11 @@ private String readProjectNameFromConsole() throws CommandLineParsingException { return StringUtil.trimToEmpty(input); } - private String readProjectFromConsole(List projects, CommandScope commandScope) throws CommandLineParsingException { + private String readProjectFromConsole(List projects, String changeLogFile) throws CommandLineParsingException { final UIService ui = Scope.getCurrentScope().getUI(); StringBuilder prompt = new StringBuilder("Registering a changelog connects Liquibase operations to a Project for monitoring and reporting.\n"); - prompt.append("Register changelog " + commandScope.getArgumentValue(CHANGELOG_FILE_ARG) + " to an existing Project, or create a new one.\n"); + prompt.append("Register changelog " + changeLogFile + " to an existing Project, or create a new one.\n"); prompt.append("Please make a selection:\n"); diff --git a/liquibase-core/src/main/java/liquibase/hub/core/MockHubService.java b/liquibase-core/src/main/java/liquibase/hub/core/MockHubService.java index ab2f8f292d4..ab8c7c82d10 100644 --- a/liquibase-core/src/main/java/liquibase/hub/core/MockHubService.java +++ b/liquibase-core/src/main/java/liquibase/hub/core/MockHubService.java @@ -20,6 +20,7 @@ public class MockHubService implements HubService { public static Date operationCreateDate; public static String apiKey = UUID.randomUUID().toString(); public static UUID organizationId = UUID.randomUUID(); + public static Integer numberOfProjectsInList = null; public List returnProjects = new ArrayList<>(); public List returnConnections; @@ -66,16 +67,28 @@ public HubChangeLog createChangeLog(HubChangeLog hubChangeLog) throws LiquibaseE @Override public List getProjects() throws LiquibaseHubException { - Project project1 = new Project(); - project1.setId(UUID.fromString("72e4bc5a-5404-45be-b9e1-280a80c98cbf")); - project1.setName("Project 1"); - project1.setCreateDate(new Date()); - - Project project2 = new Project(); - project2.setId(UUID.randomUUID()); - project2.setName("Project 2"); - project2.setCreateDate(new Date()); - return Arrays.asList(project1, project2); + if (numberOfProjectsInList == null) { + Project project1 = new Project(); + project1.setId(UUID.fromString("72e4bc5a-5404-45be-b9e1-280a80c98cbf")); + project1.setName("Project 1"); + project1.setCreateDate(new Date()); + + Project project2 = new Project(); + project2.setId(UUID.randomUUID()); + project2.setName("Project 2"); + project2.setCreateDate(new Date()); + return Arrays.asList(project1, project2); + } else { + List projects = new ArrayList<>(numberOfProjectsInList); + for(int i = 0; i < numberOfProjectsInList; i++) { + Project project = new Project(); + project.setId(UUID.randomUUID()); + project.setName("Project " + i + 1); + project.setCreateDate(new Date()); + projects.add(project); + } + return projects; + } } @Override From 04d5e358d55676d33ae72ed5a501d306c75667e2 Mon Sep 17 00:00:00 2001 From: Steven Massaro Date: Mon, 10 Jan 2022 14:59:24 -0600 Subject: [PATCH 29/37] add ability to send operation events to hub with a specific organization ID (DAT-8803) (#2350) --- .../src/main/java/liquibase/hub/HubService.java | 2 ++ .../src/main/java/liquibase/hub/HubServiceFactory.java | 5 +++++ .../src/main/java/liquibase/hub/core/MockHubService.java | 5 +++++ .../main/java/liquibase/hub/core/StandardHubService.java | 8 ++++++-- 4 files changed, 18 insertions(+), 2 deletions(-) diff --git a/liquibase-core/src/main/java/liquibase/hub/HubService.java b/liquibase-core/src/main/java/liquibase/hub/HubService.java index bca034a4f5b..bf3b1dad4a7 100644 --- a/liquibase-core/src/main/java/liquibase/hub/HubService.java +++ b/liquibase-core/src/main/java/liquibase/hub/HubService.java @@ -58,6 +58,8 @@ public interface HubService extends Plugin, PrioritizedService { */ String shortenLink(String url) throws LiquibaseException; + OperationEvent sendOperationEvent(Operation operation, OperationEvent operationEvent, UUID organizationId) throws LiquibaseException; + void sendOperationChangeEvent(OperationChangeEvent operationChangeEvent) throws LiquibaseException; void sendOperationChanges(OperationChange operationChange) throws LiquibaseHubException; diff --git a/liquibase-core/src/main/java/liquibase/hub/HubServiceFactory.java b/liquibase-core/src/main/java/liquibase/hub/HubServiceFactory.java index 1a1b34a9fe6..71365e7904f 100644 --- a/liquibase-core/src/main/java/liquibase/hub/HubServiceFactory.java +++ b/liquibase-core/src/main/java/liquibase/hub/HubServiceFactory.java @@ -162,6 +162,11 @@ public String shortenLink(String url) throws LiquibaseException { return null; } + @Override + public OperationEvent sendOperationEvent(Operation operation, OperationEvent operationEvent, UUID organizationId) throws LiquibaseException { + return null; + } + @Override public void sendOperationChangeEvent(OperationChangeEvent operationChangeEvent) throws LiquibaseException { diff --git a/liquibase-core/src/main/java/liquibase/hub/core/MockHubService.java b/liquibase-core/src/main/java/liquibase/hub/core/MockHubService.java index ab8c7c82d10..cd978d7b872 100644 --- a/liquibase-core/src/main/java/liquibase/hub/core/MockHubService.java +++ b/liquibase-core/src/main/java/liquibase/hub/core/MockHubService.java @@ -196,6 +196,11 @@ public String shortenLink(String url) throws LiquibaseException { return null; } + @Override + public OperationEvent sendOperationEvent(Operation operation, OperationEvent operationEvent, UUID organizationId) throws LiquibaseException { + return null; + } + @Override public CoreInitOnboardingResponse validateOnboardingToken(String token) throws LiquibaseHubException { CoreInitOnboardingResponse response = new CoreInitOnboardingResponse(); diff --git a/liquibase-core/src/main/java/liquibase/hub/core/StandardHubService.java b/liquibase-core/src/main/java/liquibase/hub/core/StandardHubService.java index 227fc2b9264..aca7af481af 100644 --- a/liquibase-core/src/main/java/liquibase/hub/core/StandardHubService.java +++ b/liquibase-core/src/main/java/liquibase/hub/core/StandardHubService.java @@ -502,6 +502,11 @@ protected Map getCleanOperationParameters(Map or public OperationEvent sendOperationEvent(Operation operation, OperationEvent operationEvent) throws LiquibaseException { final Organization organization = getOrganization(); + return sendOperationEvent(operation, operationEvent, organization.getId()); + } + + @Override + public OperationEvent sendOperationEvent(Operation operation, OperationEvent operationEvent, UUID organizationId) throws LiquibaseException { Map requestParams = new HashMap<>(); requestParams.put("eventType", operationEvent.getEventType()); requestParams.put("startDate", operationEvent.getStartDate()); @@ -520,8 +525,7 @@ public OperationEvent sendOperationEvent(Operation operation, OperationEvent ope } } - return http.doPost("/api/v1/organizations/" + organization.getId() + "/projects/" + operation.getConnection().getProject().getId() + "/operations/" + operation.getId() + "/operation-events", requestParams, OperationEvent.class); - + return http.doPost("/api/v1/organizations/" + organizationId + ((operation.getConnection() == null || operation.getConnection().getProject() == null) ? "" : "/projects/" + operation.getConnection().getProject().getId()) + "/operations/" + operation.getId() + "/operation-events", requestParams, OperationEvent.class); } private Date convertDateToUTC(Date dateInString) { From c26cb3ca61818aae14e356e368c90ceafe4c94d0 Mon Sep 17 00:00:00 2001 From: Steven Massaro Date: Wed, 19 Jan 2022 09:11:09 -0600 Subject: [PATCH 30/37] ignore existing hub API key when making init call (issue 2) --- .../main/java/liquibase/hub/core/StandardHubService.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/liquibase-core/src/main/java/liquibase/hub/core/StandardHubService.java b/liquibase-core/src/main/java/liquibase/hub/core/StandardHubService.java index aca7af481af..acceb6eb871 100644 --- a/liquibase-core/src/main/java/liquibase/hub/core/StandardHubService.java +++ b/liquibase-core/src/main/java/liquibase/hub/core/StandardHubService.java @@ -587,7 +587,14 @@ public void sendOperationChanges(OperationChange operationChange) throws Liquiba @Override public CoreInitOnboardingResponse validateOnboardingToken(String token) throws LiquibaseHubException { - return http.doPost("/api/v1/init", Collections.singletonMap("onboardingToken", token), CoreInitOnboardingResponse.class); + // This call does not use authentication, so we purposefully override any existing hub API key with empty string. + Map hubApiKeyScopeValues = Collections.singletonMap(HubConfiguration.LIQUIBASE_HUB_API_KEY.getKey(), ""); + try { + return Scope.child(hubApiKeyScopeValues, () -> http.doPost("/api/v1/init", Collections.singletonMap("onboardingToken", token), CoreInitOnboardingResponse.class)); + } catch (Exception e) { + Scope.getCurrentScope().getLog(getClass()).severe("Failed to call Hub to validate onboarding token", e); + throw new LiquibaseHubException(e); + } } /** From 6a819645b5075bf98d50c911cca6f756acb72907 Mon Sep 17 00:00:00 2001 From: Steven Massaro Date: Wed, 26 Jan 2022 12:32:15 -0600 Subject: [PATCH 31/37] allow init hub to operate with a changelog that has already been registered with the current organization --- .../core/RegisterChangelogCommandStep.java | 5 ++-- .../ChangeLogAlreadyRegisteredException.java | 27 +++++++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) create mode 100644 liquibase-core/src/main/java/liquibase/exception/ChangeLogAlreadyRegisteredException.java diff --git a/liquibase-core/src/main/java/liquibase/command/core/RegisterChangelogCommandStep.java b/liquibase-core/src/main/java/liquibase/command/core/RegisterChangelogCommandStep.java index 819efb846cf..1ac79a0d2ae 100644 --- a/liquibase-core/src/main/java/liquibase/command/core/RegisterChangelogCommandStep.java +++ b/liquibase-core/src/main/java/liquibase/command/core/RegisterChangelogCommandStep.java @@ -5,6 +5,7 @@ import liquibase.changelog.ChangelogRewriter; import liquibase.changelog.DatabaseChangeLog; import liquibase.command.*; +import liquibase.exception.ChangeLogAlreadyRegisteredException; import liquibase.exception.CommandExecutionException; import liquibase.exception.CommandLineParsingException; import liquibase.exception.LiquibaseException; @@ -93,11 +94,11 @@ public void doRegisterChangelog(String changeLogFile, UUID hubProjectId, String throw new CommandExecutionException("Changelog '" + changeLogFile + "' is already registered with changeLogId '" + changeLogId + "' to project '" + hubChangeLog.getProject().getName() + "' with project ID '" + hubChangeLog.getProject().getId().toString() + "'.\n" + - "For more information visit https://docs.liquibase.com."); + "For more information visit https://docs.liquibase.com.", new ChangeLogAlreadyRegisteredException(hubChangeLog)); } else { throw new CommandExecutionException("Changelog '" + changeLogFile + "' is already registered with changeLogId '" + changeLogId + "'.\n" + - "For more information visit https://docs.liquibase.com."); + "For more information visit https://docs.liquibase.com.", new ChangeLogAlreadyRegisteredException()); } } diff --git a/liquibase-core/src/main/java/liquibase/exception/ChangeLogAlreadyRegisteredException.java b/liquibase-core/src/main/java/liquibase/exception/ChangeLogAlreadyRegisteredException.java new file mode 100644 index 00000000000..e59fa0e6d87 --- /dev/null +++ b/liquibase-core/src/main/java/liquibase/exception/ChangeLogAlreadyRegisteredException.java @@ -0,0 +1,27 @@ +package liquibase.exception; + +import liquibase.hub.model.HubChangeLog; + +/** + * Exception class indicating that a particular changelog has already been registered with Hub. + */ +public class ChangeLogAlreadyRegisteredException extends Exception { + + /** + * If present, the changelog metadata from Hub. If null, it can be assumed that the changelog has been registered + * with some organization which the current API key cannot access. + */ + private final HubChangeLog hubChangeLog; + + public ChangeLogAlreadyRegisteredException() { + this(null); + } + + public ChangeLogAlreadyRegisteredException(HubChangeLog hubChangeLog) { + this.hubChangeLog = hubChangeLog; + } + + public HubChangeLog getHubChangeLog() { + return hubChangeLog; + } +} From d0b5d6e3b1bebfee9f5d16372214bae4cd10591b Mon Sep 17 00:00:00 2001 From: Steven Massaro Date: Tue, 1 Feb 2022 07:47:47 -0600 Subject: [PATCH 32/37] support registering value provider in command tests (DAT-9153) (#2436) --- .../testing/command/CommandTests.groovy | 4 ++ .../SetupConfigurationValueProvider.java | 41 +++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/setup/SetupConfigurationValueProvider.java diff --git a/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/command/CommandTests.groovy b/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/command/CommandTests.groovy index 9cfb609bfc0..403c33c7415 100644 --- a/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/command/CommandTests.groovy +++ b/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/command/CommandTests.groovy @@ -866,6 +866,10 @@ Long Description: ${commandDefinition.getLongDescription() ?: "NOT SET"} this.setups.add(new SetupCreateTempResources(originalFile, newFile, baseDir)) } + void registerValueProvider(Closure configurationValueProvider) { + this.setups.add(new SetupConfigurationValueProvider(configurationValueProvider)) + } + /** * @param fileLastModifiedDate if not null, the newly created file's last modified date will be set to this value */ diff --git a/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/setup/SetupConfigurationValueProvider.java b/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/setup/SetupConfigurationValueProvider.java new file mode 100644 index 00000000000..60fbd6e1f94 --- /dev/null +++ b/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/setup/SetupConfigurationValueProvider.java @@ -0,0 +1,41 @@ +package liquibase.extension.testing.setup; + +import groovy.lang.Closure; +import liquibase.Scope; +import liquibase.configuration.ConfigurationValueProvider; +import liquibase.configuration.LiquibaseConfiguration; + +/** + * Setup class allowing registration of custom configuration value providers, and also providing automatic cleanup. + */ +public class SetupConfigurationValueProvider extends TestSetup { + + private final Closure configurationValueProvider; + /** + * Once the configuration value provider has been instantiated, save it here. + */ + private ConfigurationValueProvider actualConfigurationValueProvider = null; + + /** + * Create a new configuration value provider. + * @param configurationValueProvider This is a closure so that instantiation can be "lazy", such that you can + * create configuration value providers which depend on other TestSetup resources, + * like newly created files, for example. + */ + public SetupConfigurationValueProvider(Closure configurationValueProvider) { + this.configurationValueProvider = configurationValueProvider; + } + + @Override + public void setup(TestSetupEnvironment testSetupEnvironment) throws Exception { + final LiquibaseConfiguration liquibaseConfiguration = Scope.getCurrentScope().getSingleton(LiquibaseConfiguration.class); + actualConfigurationValueProvider = configurationValueProvider.call(); + liquibaseConfiguration.registerProvider(actualConfigurationValueProvider); + } + + @Override + public void cleanup() { + LiquibaseConfiguration lbConf = Scope.getCurrentScope().getSingleton(LiquibaseConfiguration.class); + lbConf.unregisterProvider(actualConfigurationValueProvider); + } +} From e86a3276170624b2ca46245e5db0dad7be86bb64 Mon Sep 17 00:00:00 2001 From: Steven Massaro Date: Tue, 1 Feb 2022 13:30:50 -0600 Subject: [PATCH 33/37] add start-H2 message to error message in JDBC connection creation --- .../database/jvm/JdbcConnection.java | 9 +++-- .../liquibase/maven/plugins/MavenUtils.java | 36 ------------------- 2 files changed, 7 insertions(+), 38 deletions(-) diff --git a/liquibase-core/src/main/java/liquibase/database/jvm/JdbcConnection.java b/liquibase-core/src/main/java/liquibase/database/jvm/JdbcConnection.java index a89509a548b..b87b477f4fc 100644 --- a/liquibase-core/src/main/java/liquibase/database/jvm/JdbcConnection.java +++ b/liquibase-core/src/main/java/liquibase/database/jvm/JdbcConnection.java @@ -40,13 +40,18 @@ public int getPriority() { @Override public void open(String url, Driver driverObject, Properties driverProperties) throws DatabaseException { + String driverClassName = driverObject.getClass().getName(); + String errorMessage = "Connection could not be created to " + url + " with driver " + driverClassName; try { this.con = driverObject.connect(url, driverProperties); if (this.con == null) { - throw new DatabaseException("Connection could not be created to " + url + " with driver " + driverObject.getClass().getName() + ". Possibly the wrong driver for the given database URL"); + throw new DatabaseException(errorMessage + ". Possibly the wrong driver for the given database URL"); } } catch (SQLException sqle) { - throw new DatabaseException("Connection could not be created to " + url + " with driver " + driverObject.getClass().getName() + ". " + sqle.getMessage()); + if (driverClassName.equals("org.h2.Driver")) { + errorMessage += ". Make sure your H2 database is active and accessible by opening a new terminal window, run \"liquibase init start-h2\", and then return to this terminal window to run commands"; + } + throw new DatabaseException(errorMessage + ". " + sqle.getMessage()); } } diff --git a/liquibase-maven-plugin/src/main/java/org/liquibase/maven/plugins/MavenUtils.java b/liquibase-maven-plugin/src/main/java/org/liquibase/maven/plugins/MavenUtils.java index e3e93788d9f..51573d7ead1 100644 --- a/liquibase-maven-plugin/src/main/java/org/liquibase/maven/plugins/MavenUtils.java +++ b/liquibase-maven-plugin/src/main/java/org/liquibase/maven/plugins/MavenUtils.java @@ -171,42 +171,6 @@ private static void addFile(Set urls, File f, Log log, boolean verbose) } } - public static Connection getDatabaseConnection(ClassLoader classLoader, - String driver, - String url, - String username, - String password) - throws LiquibaseException { - Driver dbDriver = null; - try { - dbDriver = (Driver) Class.forName(driver, - true, - classLoader).getConstructor().newInstance(); - } catch (ClassNotFoundException e) { - throw new LiquibaseException("Missing Class '" + e.getMessage() + "'. Database " - + "driver may not be included in the project " - + "dependencies or with wrong scope."); - } catch (ReflectiveOperationException e) { - throw new LiquibaseException("Failed to load JDBC driver, " + driver, e); - } - - Properties info = new Properties(); - info.put("user", username); - info.put("password", password); - try { - Connection connection = dbDriver.connect(url, info); - if (connection == null) { - throw new LiquibaseException("Connection could not be created to " + url - + " with driver " + dbDriver.getClass().getName() - + ". Possibly the wrong driver for the given " - + "database URL"); - } - return connection; - } catch (SQLException e) { - throw new LiquibaseException(e); - } - } - /** * Recursively searches for the field specified by the fieldName in the class and all * the super classes until it either finds it, or runs out of parents. From e76c84ec12528d224488b829f9a3f0163f46fb27 Mon Sep 17 00:00:00 2001 From: Steven Massaro Date: Mon, 7 Feb 2022 10:33:55 -0600 Subject: [PATCH 34/37] expose more hub properties for use in testing, allow generic expectations in command tests (DAT-9241) (#2476) --- .../core/RegisterChangelogCommandStep.java | 26 ++++++++++--------- .../liquibase/hub/core/MockHubService.java | 2 ++ .../testing/command/CommandTests.groovy | 8 ++++++ 3 files changed, 24 insertions(+), 12 deletions(-) diff --git a/liquibase-core/src/main/java/liquibase/command/core/RegisterChangelogCommandStep.java b/liquibase-core/src/main/java/liquibase/command/core/RegisterChangelogCommandStep.java index 1ac79a0d2ae..9ac553aa6f4 100644 --- a/liquibase-core/src/main/java/liquibase/command/core/RegisterChangelogCommandStep.java +++ b/liquibase-core/src/main/java/liquibase/command/core/RegisterChangelogCommandStep.java @@ -22,6 +22,7 @@ import liquibase.util.StringUtil; import java.io.PrintWriter; +import java.nio.file.Paths; import java.util.*; public class RegisterChangelogCommandStep extends AbstractCommandStep { @@ -74,29 +75,29 @@ public void run(CommandResultsBuilder resultsBuilder) throws Exception { doRegisterChangelog(changeLogFile, hubProjectId, hubProjectName, resultsBuilder, false); } - public void doRegisterChangelog(String changeLogFile, UUID hubProjectId, String hubProjectName, CommandResultsBuilder resultsBuilder, boolean skipPromptIfOneProject) throws LiquibaseException, CommandLineParsingException { + public void doRegisterChangelog(String changelogFilepath, UUID hubProjectId, String hubProjectName, CommandResultsBuilder resultsBuilder, boolean skipPromptIfOneProject) throws LiquibaseException, CommandLineParsingException { try (PrintWriter output = new PrintWriter(resultsBuilder.getOutputStream())) { HubChangeLog hubChangeLog; final HubService service = Scope.getCurrentScope().getSingleton(HubServiceFactory.class).getService(); // - // CHeck for existing changeLog file + // Check for existing changeLog file using the untouched changelog filepath // - DatabaseChangeLog databaseChangeLog = parseChangeLogFile(changeLogFile); + DatabaseChangeLog databaseChangeLog = parseChangeLogFile(changelogFilepath); if (databaseChangeLog == null) { - throw new CommandExecutionException("Cannot parse "+changeLogFile); + throw new CommandExecutionException("Cannot parse "+changelogFilepath); } final String changeLogId = databaseChangeLog.getChangeLogId(); if (changeLogId != null) { hubChangeLog = service.getHubChangeLog(UUID.fromString(changeLogId)); if (hubChangeLog != null) { - throw new CommandExecutionException("Changelog '" + changeLogFile + + throw new CommandExecutionException("Changelog '" + changelogFilepath + "' is already registered with changeLogId '" + changeLogId + "' to project '" + hubChangeLog.getProject().getName() + "' with project ID '" + hubChangeLog.getProject().getId().toString() + "'.\n" + "For more information visit https://docs.liquibase.com.", new ChangeLogAlreadyRegisteredException(hubChangeLog)); } else { - throw new CommandExecutionException("Changelog '" + changeLogFile + + throw new CommandExecutionException("Changelog '" + changelogFilepath + "' is already registered with changeLogId '" + changeLogId + "'.\n" + "For more information visit https://docs.liquibase.com.", new ChangeLogAlreadyRegisteredException()); } @@ -122,19 +123,20 @@ public void doRegisterChangelog(String changeLogFile, UUID hubProjectId, String } output.print("\nProject '" + project.getName() + "' created with project ID '" + project.getId() + "'.\n\n"); } else { - project = retrieveOrCreateProject(service, changeLogFile, skipPromptIfOneProject); + project = retrieveOrCreateProject(service, changelogFilepath, skipPromptIfOneProject); if (project == null) { - throw new CommandExecutionException("Your changelog " + changeLogFile + " was not registered to any Liquibase Hub project. You can still run Liquibase commands, but no data will be saved in your Liquibase Hub account for monitoring or reports. Learn more at https://hub.liquibase.com."); + throw new CommandExecutionException("Your changelog " + changelogFilepath + " was not registered to any Liquibase Hub project. You can still run Liquibase commands, but no data will be saved in your Liquibase Hub account for monitoring or reports. Learn more at https://hub.liquibase.com."); } } // // Go create the Hub Changelog // + String changelogFilename = Paths.get(databaseChangeLog.getFilePath()).getFileName().toString(); HubChangeLog newChangeLog = new HubChangeLog(); newChangeLog.setProject(project); - newChangeLog.setFileName(databaseChangeLog.getFilePath()); - newChangeLog.setName(databaseChangeLog.getFilePath()); + newChangeLog.setFileName(changelogFilename); + newChangeLog.setName(changelogFilename); hubChangeLog = service.createChangeLog(newChangeLog); @@ -143,11 +145,11 @@ public void doRegisterChangelog(String changeLogFile, UUID hubProjectId, String // Add the registered changelog ID to the results so that // the caller can use it // - ChangelogRewriter.ChangeLogRewriterResult changeLogRewriterResult = ChangelogRewriter.addChangeLogId(changeLogFile, hubChangeLog.getId().toString(), databaseChangeLog); + ChangelogRewriter.ChangeLogRewriterResult changeLogRewriterResult = ChangelogRewriter.addChangeLogId(changelogFilepath, hubChangeLog.getId().toString(), databaseChangeLog); if (changeLogRewriterResult.success) { Scope.getCurrentScope().getLog(RegisterChangelogCommandStep.class).info(changeLogRewriterResult.message); - output.println("* Changelog file '" + changeLogFile + "' with changelog ID '" + hubChangeLog.getId().toString() + "' has been " + + output.println("* Changelog file '" + changelogFilepath + "' with changelog ID '" + hubChangeLog.getId().toString() + "' has been " + "registered to Project "+project.getName() ); resultsBuilder.addResult("statusCode", 0); resultsBuilder.addResult(REGISTERED_CHANGELOG_ID.getName(), hubChangeLog.getId().toString()); diff --git a/liquibase-core/src/main/java/liquibase/hub/core/MockHubService.java b/liquibase-core/src/main/java/liquibase/hub/core/MockHubService.java index cd978d7b872..d93dc7f561b 100644 --- a/liquibase-core/src/main/java/liquibase/hub/core/MockHubService.java +++ b/liquibase-core/src/main/java/liquibase/hub/core/MockHubService.java @@ -21,6 +21,7 @@ public class MockHubService implements HubService { public static String apiKey = UUID.randomUUID().toString(); public static UUID organizationId = UUID.randomUUID(); public static Integer numberOfProjectsInList = null; + public static HubChangeLog lastCreatedChangelog = null; public List returnProjects = new ArrayList<>(); public List returnConnections; @@ -62,6 +63,7 @@ public HubChangeLog createChangeLog(HubChangeLog hubChangeLog) throws LiquibaseE randomUUID = UUID.randomUUID(); } hubChangeLog.setId(randomUUID); + lastCreatedChangelog = hubChangeLog; return hubChangeLog; } diff --git a/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/command/CommandTests.groovy b/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/command/CommandTests.groovy index 40bfc7f7ecf..b15cf912428 100644 --- a/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/command/CommandTests.groovy +++ b/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/command/CommandTests.groovy @@ -337,6 +337,9 @@ Long Description: ${commandDefinition.getLongDescription() ?: "NOT SET"} if (testDef.expectFileToNotExist != null) { assert !testDef.expectFileToNotExist.exists(): "File '${testDef.expectFileToNotExist.getAbsolutePath()}' should not exist" } + if (testDef.expectations != null) { + testDef.expectations.call() + } } finally { if (testDef.setup != null) { for (def setup : testDef.setup) { @@ -627,6 +630,7 @@ Long Description: ${commandDefinition.getLongDescription() ?: "NOT SET"} private Map arguments = new HashMap<>() private Map expectedFileContent = new HashMap<>() private Map expectedDatabaseContent = new HashMap<>() + private Closure expectations = null; private List setup @@ -689,6 +693,10 @@ Long Description: ${commandDefinition.getLongDescription() ?: "NOT SET"} this.expectedFileContent = content } + def setExpectations(Closure expectations) { + this.expectations = expectations; + } + def setExpectedDatabaseContent(Map content) { this.expectedDatabaseContent = content } From 7e08fadaa8dab4859cde98875583b9eea0dc3b4f Mon Sep 17 00:00:00 2001 From: Steven Massaro Date: Wed, 9 Feb 2022 07:40:10 -0600 Subject: [PATCH 35/37] test --- .../liquibase/extension/testing/command/CommandTests.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/command/CommandTests.groovy b/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/command/CommandTests.groovy index b15cf912428..8bf5d5441b5 100644 --- a/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/command/CommandTests.groovy +++ b/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/command/CommandTests.groovy @@ -488,7 +488,7 @@ Long Description: ${commandDefinition.getLongDescription() ?: "NOT SET"} def path = "src/test/resources/liquibase/extension/testing/command/" try { - (path as File).eachFileRecurse { + (path as File). eachFileRecurse { if (!it.name.endsWith("test.groovy")) { return } From 7507a4973c330341135bbc017cb07c4c1ae22ad0 Mon Sep 17 00:00:00 2001 From: Steven Massaro Date: Wed, 9 Feb 2022 07:59:44 -0600 Subject: [PATCH 36/37] Revert "test" This reverts commit 7e08fadaa8dab4859cde98875583b9eea0dc3b4f. --- .../liquibase/extension/testing/command/CommandTests.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/command/CommandTests.groovy b/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/command/CommandTests.groovy index 8bf5d5441b5..b15cf912428 100644 --- a/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/command/CommandTests.groovy +++ b/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/command/CommandTests.groovy @@ -488,7 +488,7 @@ Long Description: ${commandDefinition.getLongDescription() ?: "NOT SET"} def path = "src/test/resources/liquibase/extension/testing/command/" try { - (path as File). eachFileRecurse { + (path as File).eachFileRecurse { if (!it.name.endsWith("test.groovy")) { return } From bcc04dcae0c57585ddcf89f5b9ebc34372b84e46 Mon Sep 17 00:00:00 2001 From: Steven Massaro Date: Mon, 14 Feb 2022 08:37:03 -0600 Subject: [PATCH 37/37] add run before test to CommandTests and expose more MockHubService properties (DAT-9165) (#2512) --- .../main/java/liquibase/hub/core/MockHubService.java | 3 ++- .../extension/testing/command/CommandTests.groovy | 11 ++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/liquibase-core/src/main/java/liquibase/hub/core/MockHubService.java b/liquibase-core/src/main/java/liquibase/hub/core/MockHubService.java index d93dc7f561b..b4b3def74e1 100644 --- a/liquibase-core/src/main/java/liquibase/hub/core/MockHubService.java +++ b/liquibase-core/src/main/java/liquibase/hub/core/MockHubService.java @@ -26,7 +26,7 @@ public class MockHubService implements HubService { public List returnProjects = new ArrayList<>(); public List returnConnections; public List returnChangeLogs = new ArrayList<>(); - public SortedMap sentObjects = new TreeMap<>(); + public static SortedMap sentObjects = new TreeMap<>(); public boolean online = true; @Override @@ -200,6 +200,7 @@ public String shortenLink(String url) throws LiquibaseException { @Override public OperationEvent sendOperationEvent(Operation operation, OperationEvent operationEvent, UUID organizationId) throws LiquibaseException { + sentObjects.computeIfAbsent("sendOperationEvent", k -> new ArrayList<>()).add(operationEvent); return null; } diff --git a/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/command/CommandTests.groovy b/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/command/CommandTests.groovy index b15cf912428..1e6345bfac3 100644 --- a/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/command/CommandTests.groovy +++ b/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/command/CommandTests.groovy @@ -277,6 +277,9 @@ Long Description: ${commandDefinition.getLongDescription() ?: "NOT SET"} (Scope.Attr.logService.name()) : logService ], { try { + if (testDef.commandTestDefinition.beforeMethodInvocation != null) { + testDef.commandTestDefinition.beforeMethodInvocation.call() + } def returnValue = commandScope.execute() assert testDef.expectedException == null : "An exception was expected but the command completed successfully" return returnValue @@ -581,7 +584,13 @@ Long Description: ${commandDefinition.getLongDescription() ?: "NOT SET"} * the same scope as the command that is run for the test. This method will always be called, regardless of * exceptions thrown from within the test. */ - Callable afterMethodInvocation + Closure afterMethodInvocation + /** + * An optional method that will be called before the execution of each run command. This is executed within + * the same scope as the command that is run for the test. Exceptions thrown from this method will cause the + * test to fail. + */ + Closure beforeMethodInvocation void run(@DelegatesTo(RunTestDefinition) Closure testClosure) { run(null, testClosure)