Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

point users to init project command if they forget a supported argument in any command (DAT-8641) #2356

Merged
merged 7 commits into from Jan 19, 2022
Expand Up @@ -2,6 +2,9 @@

import liquibase.command.CommandResults;
import liquibase.command.CommandScope;
import liquibase.command.CommonArgumentNames;
import liquibase.exception.CommandValidationException;
import liquibase.exception.MissingRequiredArgumentException;
import liquibase.util.StringUtil;
import picocli.CommandLine;

Expand All @@ -10,7 +13,10 @@
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.stream.Collectors;
import java.util.stream.Stream;

class CommandRunner implements Callable<CommandResults> {

Expand Down Expand Up @@ -43,6 +49,17 @@ public CommandResults call() throws Exception {
}

return commandScope.execute();
} catch (CommandValidationException cve) {
Throwable cause = cve.getCause();
if (cause instanceof MissingRequiredArgumentException) {
// This is a list of the arguments which the init project command supports. The thinking here is that if the user
// forgets to supply one of these arguments, we're going to remind them about the init project command, which
// can help them figure out what they should be providing here.
final Set<String> initProjectArguments = Stream.of(CommonArgumentNames.CHANGELOG_FILE, CommonArgumentNames.URL, CommonArgumentNames.USERNAME, CommonArgumentNames.PASSWORD).map(CommonArgumentNames::getArgumentName).collect(Collectors.toSet());
throw new CommandValidationException(cve.getMessage() + (initProjectArguments.contains(((MissingRequiredArgumentException) cause).getArgumentName()) ? ". If you need to configure new liquibase project files and arguments, run the 'liquibase init project' command." : ""));
} else {
throw cve;
}
} finally {
if (outputStream != null) {
outputStream.flush();
Expand Down
Expand Up @@ -4,6 +4,7 @@
import liquibase.configuration.ConfigurationValueConverter;
import liquibase.configuration.ConfigurationValueObfuscator;
import liquibase.exception.CommandValidationException;
import liquibase.exception.MissingRequiredArgumentException;
import liquibase.integration.commandline.LiquibaseCommandLineConfiguration;
import liquibase.util.ObjectUtil;

Expand Down Expand Up @@ -113,7 +114,7 @@ public ConfigurationValueObfuscator<DataType> getValueObfuscator() {
public void validate(CommandScope commandScope) throws CommandValidationException {
final DataType currentValue = commandScope.getArgumentValue(this);
if (this.isRequired() && currentValue == null) {
throw new CommandValidationException(LiquibaseCommandLineConfiguration.ARGUMENT_CONVERTER.getCurrentValue().convert(this.getName()), "missing required argument");
throw new CommandValidationException(LiquibaseCommandLineConfiguration.ARGUMENT_CONVERTER.getCurrentValue().convert(this.getName()), "missing required argument", new MissingRequiredArgumentException(this.getName()));
}
}

Expand Down
Expand Up @@ -21,6 +21,13 @@ public <DataType> CommandArgumentDefinition.Building<DataType> argument(String n
return new CommandArgumentDefinition.Building<>(commandNames, new CommandArgumentDefinition<>(name, type));
}

/**
* Starts the building of a new {@link CommandArgumentDefinition}.
*/
public <DataType> CommandArgumentDefinition.Building<DataType> argument(CommonArgumentNames argument, Class<DataType> type) {
return new CommandArgumentDefinition.Building<>(commandNames, new CommandArgumentDefinition<>(argument.getArgumentName(), type));
}

/**
* Starts the building of a new {@link CommandResultDefinition}.
*/
Expand Down
@@ -0,0 +1,21 @@
package liquibase.command;

/**
* A common place to store commonly used command argument names.
*/
public enum CommonArgumentNames {
USERNAME("username"),
PASSWORD("password"),
URL("url"),
CHANGELOG_FILE("changelogFile");

private final String argumentName;

CommonArgumentNames(String argumentName) {
this.argumentName = argumentName;
}

public String getArgumentName() {
return argumentName;
}
}
Expand Up @@ -20,9 +20,9 @@ public class CalculateChecksumCommandStep extends AbstractCliWrapperCommandStep

static {
CommandBuilder builder = new CommandBuilder(COMMAND_NAME);
CHANGELOG_FILE_ARG = builder.argument("changelogFile", String.class).required()
CHANGELOG_FILE_ARG = builder.argument(CommonArgumentNames.CHANGELOG_FILE, String.class).required()
.description("The root changelog file").build();
URL_ARG = builder.argument("url", String.class).required()
URL_ARG = builder.argument(CommonArgumentNames.URL, String.class).required()
.description("The JDBC database connection URL").build();
DEFAULT_SCHEMA_NAME_ARG = builder.argument("defaultSchemaName", String.class)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While they aren't as commonly used, defaultSchemaName and driverPropertiesFile are part of the set of common arguments across commands.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was contemplating changing around the other arguments as well. I'll do that in another PR to keep things nice and separate.

.description("The default schema name to use for the database connection").build();
Expand All @@ -32,9 +32,9 @@ public class CalculateChecksumCommandStep extends AbstractCliWrapperCommandStep
.description("The JDBC driver class").build();
DRIVER_PROPERTIES_FILE_ARG = builder.argument("driverPropertiesFile", String.class)
.description("The JDBC driver properties file").build();
USERNAME_ARG = builder.argument("username", String.class)
USERNAME_ARG = builder.argument(CommonArgumentNames.USERNAME, String.class)
.description("The database username").build();
PASSWORD_ARG = builder.argument("password", String.class)
PASSWORD_ARG = builder.argument(CommonArgumentNames.PASSWORD, String.class)
.description("The database password")
.setValueObfuscator(ConfigurationValueObfuscator.STANDARD)
.build();
Expand Down
Expand Up @@ -21,9 +21,9 @@ public class ChangelogSyncCommandStep extends AbstractCliWrapperCommandStep {

static {
CommandBuilder builder = new CommandBuilder(COMMAND_NAME);
CHANGELOG_FILE_ARG = builder.argument("changelogFile", String.class).required()
CHANGELOG_FILE_ARG = builder.argument(CommonArgumentNames.CHANGELOG_FILE, String.class).required()
.description("The root changelog file").build();
URL_ARG = builder.argument("url", String.class).required()
URL_ARG = builder.argument(CommonArgumentNames.URL, String.class).required()
.description("The JDBC database connection URL").build();
DEFAULT_SCHEMA_NAME_ARG = builder.argument("defaultSchemaName", String.class)
.description("The default schema name to use for the database connection").build();
Expand All @@ -33,9 +33,9 @@ public class ChangelogSyncCommandStep extends AbstractCliWrapperCommandStep {
.description("The JDBC driver class").build();
DRIVER_PROPERTIES_FILE_ARG = builder.argument("driverPropertiesFile", String.class)
.description("The JDBC driver properties file").build();
USERNAME_ARG = builder.argument("username", String.class)
USERNAME_ARG = builder.argument(CommonArgumentNames.USERNAME, String.class)
.description("The database username").build();
PASSWORD_ARG = builder.argument("password", String.class)
PASSWORD_ARG = builder.argument(CommonArgumentNames.PASSWORD, String.class)
.description("The database password")
.setValueObfuscator(ConfigurationValueObfuscator.STANDARD)
.build();
Expand Down
Expand Up @@ -21,9 +21,9 @@ public class ChangelogSyncSqlCommandStep extends AbstractCliWrapperCommandStep {

static {
CommandBuilder builder = new CommandBuilder(COMMAND_NAME);
CHANGELOG_FILE_ARG = builder.argument("changelogFile", String.class).required()
CHANGELOG_FILE_ARG = builder.argument(CommonArgumentNames.CHANGELOG_FILE, String.class).required()
.description("The root changelog file").build();
URL_ARG = builder.argument("url", String.class).required()
URL_ARG = builder.argument(CommonArgumentNames.URL, String.class).required()
.description("The JDBC database connection URL").build();
DEFAULT_SCHEMA_NAME_ARG = builder.argument("defaultSchemaName", String.class)
.description("The default schema name to use for the database connection").build();
Expand All @@ -33,9 +33,9 @@ public class ChangelogSyncSqlCommandStep extends AbstractCliWrapperCommandStep {
.description("The JDBC driver class").build();
DRIVER_PROPERTIES_FILE_ARG = builder.argument("driverPropertiesFile", String.class)
.description("The JDBC driver properties file").build();
USERNAME_ARG = builder.argument("username", String.class)
USERNAME_ARG = builder.argument(CommonArgumentNames.USERNAME, String.class)
.description("The database username").build();
PASSWORD_ARG = builder.argument("password", String.class)
PASSWORD_ARG = builder.argument(CommonArgumentNames.PASSWORD, String.class)
.setValueObfuscator(ConfigurationValueObfuscator.STANDARD)
.description("The database password").build();
LABELS_ARG = builder.argument("labels", String.class)
Expand Down
Expand Up @@ -22,9 +22,9 @@ public class ChangelogSyncToTagCommandStep extends AbstractCliWrapperCommandStep

static {
CommandBuilder builder = new CommandBuilder(COMMAND_NAME);
CHANGELOG_FILE_ARG = builder.argument("changelogFile", String.class).required()
CHANGELOG_FILE_ARG = builder.argument(CommonArgumentNames.CHANGELOG_FILE, String.class).required()
.description("The root changelog file").build();
URL_ARG = builder.argument("url", String.class).required()
URL_ARG = builder.argument(CommonArgumentNames.URL, String.class).required()
.description("The JDBC database connection URL").build();
DEFAULT_SCHEMA_NAME_ARG = builder.argument("defaultSchemaName", String.class)
.description("The default schema name to use for the database connection").build();
Expand All @@ -34,9 +34,9 @@ public class ChangelogSyncToTagCommandStep extends AbstractCliWrapperCommandStep
.description("The JDBC driver class").build();
DRIVER_PROPERTIES_FILE_ARG = builder.argument("driverPropertiesFile", String.class)
.description("The JDBC driver properties file").build();
USERNAME_ARG = builder.argument("username", String.class)
USERNAME_ARG = builder.argument(CommonArgumentNames.USERNAME, String.class)
.description("The database username").build();
PASSWORD_ARG = builder.argument("password", String.class)
PASSWORD_ARG = builder.argument(CommonArgumentNames.PASSWORD, String.class)
.setValueObfuscator(ConfigurationValueObfuscator.STANDARD)
.description("The database password").build();
LABELS_ARG = builder.argument("labels", String.class)
Expand Down
Expand Up @@ -22,9 +22,9 @@ public class ChangelogSyncToTagSqlCommandStep extends AbstractCliWrapperCommandS

static {
CommandBuilder builder = new CommandBuilder(COMMAND_NAME);
CHANGELOG_FILE_ARG = builder.argument("changelogFile", String.class).required()
CHANGELOG_FILE_ARG = builder.argument(CommonArgumentNames.CHANGELOG_FILE, String.class).required()
.description("The root changelog file").build();
URL_ARG = builder.argument("url", String.class).required()
URL_ARG = builder.argument(CommonArgumentNames.URL, String.class).required()
.description("The JDBC database connection URL").build();
DEFAULT_SCHEMA_NAME_ARG = builder.argument("defaultSchemaName", String.class)
.description("The default schema name to use for the database connection").build();
Expand All @@ -34,9 +34,9 @@ public class ChangelogSyncToTagSqlCommandStep extends AbstractCliWrapperCommandS
.description("The JDBC driver class").build();
DRIVER_PROPERTIES_FILE_ARG = builder.argument("driverPropertiesFile", String.class)
.description("The JDBC driver properties file").build();
USERNAME_ARG = builder.argument("username", String.class)
USERNAME_ARG = builder.argument(CommonArgumentNames.USERNAME, String.class)
.description("The database username").build();
PASSWORD_ARG = builder.argument("password", String.class)
PASSWORD_ARG = builder.argument(CommonArgumentNames.PASSWORD, String.class)
.setValueObfuscator(ConfigurationValueObfuscator.STANDARD)
.description("The database password").build();
LABELS_ARG = builder.argument("labels", String.class)
Expand Down
Expand Up @@ -18,7 +18,7 @@ public class ClearChecksumsCommandStep extends AbstractCliWrapperCommandStep {

static {
CommandBuilder builder = new CommandBuilder(COMMAND_NAME);
URL_ARG = builder.argument("url", String.class).required()
URL_ARG = builder.argument(CommonArgumentNames.URL, String.class).required()
.description("The JDBC database connection URL").build();
DEFAULT_SCHEMA_NAME_ARG = builder.argument("defaultSchemaName", String.class)
.description("The default schema name to use for the database connection").build();
Expand All @@ -28,9 +28,9 @@ public class ClearChecksumsCommandStep extends AbstractCliWrapperCommandStep {
.description("The JDBC driver class").build();
DRIVER_PROPERTIES_FILE_ARG = builder.argument("driverPropertiesFile", String.class)
.description("The JDBC driver properties file").build();
USERNAME_ARG = builder.argument("username", String.class)
USERNAME_ARG = builder.argument(CommonArgumentNames.USERNAME, String.class)
.description("The database username").build();
PASSWORD_ARG = builder.argument("password", String.class)
PASSWORD_ARG = builder.argument(CommonArgumentNames.PASSWORD, String.class)
.setValueObfuscator(ConfigurationValueObfuscator.STANDARD)
.description("The database password").build();
}
Expand Down
Expand Up @@ -20,9 +20,9 @@ public class DbDocCommandStep extends AbstractCliWrapperCommandStep {

static {
CommandBuilder builder = new CommandBuilder(COMMAND_NAME);
CHANGELOG_FILE_ARG = builder.argument("changelogFile", String.class)
CHANGELOG_FILE_ARG = builder.argument(CommonArgumentNames.CHANGELOG_FILE, String.class)
.description("The root changelog").required().build();
URL_ARG = builder.argument("url", String.class).required()
URL_ARG = builder.argument(CommonArgumentNames.URL, String.class).required()
.description("The JDBC database connection URL").build();
DEFAULT_SCHEMA_NAME_ARG = builder.argument("defaultSchemaName", String.class)
.description("The default schema name to use for the database connection").build();
Expand All @@ -32,9 +32,9 @@ public class DbDocCommandStep extends AbstractCliWrapperCommandStep {
.description("The JDBC driver class").build();
DRIVER_PROPERTIES_FILE_ARG = builder.argument("driverPropertiesFile", String.class)
.description("The JDBC driver properties file").build();
USERNAME_ARG = builder.argument("username", String.class)
USERNAME_ARG = builder.argument(CommonArgumentNames.USERNAME, String.class)
.description("The database username").build();
PASSWORD_ARG = builder.argument("password", String.class)
PASSWORD_ARG = builder.argument(CommonArgumentNames.PASSWORD, String.class)
.description("The database password")
.setValueObfuscator(ConfigurationValueObfuscator.STANDARD)
.build();
Expand Down
Expand Up @@ -25,7 +25,7 @@ public class DeactivateChangelogCommandStep extends AbstractCommandStep {

static {
final CommandBuilder builder = new CommandBuilder(COMMAND_NAME);
CHANGELOG_FILE_ARG = builder.argument("changelogFile", String.class).required()
CHANGELOG_FILE_ARG = builder.argument(CommonArgumentNames.CHANGELOG_FILE, String.class).required()
.description("The root changelog").build();
}

Expand Down
Expand Up @@ -42,7 +42,7 @@ public class DiffChangelogCommandStep extends AbstractCliWrapperCommandStep {
.description("The reference default schema name to use for the database connection").build();
REFERENCE_DEFAULT_CATALOG_NAME_ARG = builder.argument("referenceDefaultCatalogName", String.class)
.description("The reference default catalog name to use for the database connection").build();
URL_ARG = builder.argument("url", String.class).required()
URL_ARG = builder.argument(CommonArgumentNames.URL, String.class).required()
.description("The JDBC target database connection URL").build();
DEFAULT_SCHEMA_NAME_ARG = builder.argument("defaultSchemaName", String.class)
.description("The default schema name to use for the database connection").build();
Expand All @@ -52,13 +52,13 @@ public class DiffChangelogCommandStep extends AbstractCliWrapperCommandStep {
.description("The JDBC driver class").build();
DRIVER_PROPERTIES_FILE_ARG = builder.argument("driverPropertiesFile", String.class)
.description("The JDBC driver properties file").build();
USERNAME_ARG = builder.argument("username", String.class)
USERNAME_ARG = builder.argument(CommonArgumentNames.USERNAME, String.class)
.description("The target database username").build();
PASSWORD_ARG = builder.argument("password", String.class)
PASSWORD_ARG = builder.argument(CommonArgumentNames.PASSWORD, String.class)
.description("The target database password")
.setValueObfuscator(ConfigurationValueObfuscator.STANDARD)
.build();
CHANGELOG_FILE_ARG = builder.argument("changelogFile", String.class).required()
CHANGELOG_FILE_ARG = builder.argument(CommonArgumentNames.CHANGELOG_FILE, String.class).required()
.description("Changelog file to write results").build();
EXCLUDE_OBJECTS_ARG = builder.argument("excludeObjects", String.class)
.description("Objects to exclude from diff").build();
Expand Down
Expand Up @@ -33,11 +33,11 @@ public class DiffCommandStep extends AbstractCliWrapperCommandStep {
.description("The reference database password")
.setValueObfuscator(ConfigurationValueObfuscator.STANDARD)
.build();
URL_ARG = builder.argument("url", String.class).required()
URL_ARG = builder.argument(CommonArgumentNames.URL, String.class).required()
.description("The JDBC target database connection URL").build();
USERNAME_ARG = builder.argument("username", String.class)
USERNAME_ARG = builder.argument(CommonArgumentNames.USERNAME, String.class)
.description("The target database username").build();
PASSWORD_ARG = builder.argument("password", String.class)
PASSWORD_ARG = builder.argument(CommonArgumentNames.PASSWORD, String.class)
.description("The target database password")
.setValueObfuscator(ConfigurationValueObfuscator.STANDARD)
.build();
Expand Down