Polish 'Support both kebab-case and camelCase as Spring init CLI Options'

Refine the command so that camelCase options are supported but not
advertised.

See gh-28138
pull/28448/head
Phillip Webb 3 years ago
parent ad3473208f
commit c384fbd14e

@ -20,7 +20,10 @@ import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import joptsimple.OptionSet; import joptsimple.OptionSet;
import joptsimple.OptionSpec; import joptsimple.OptionSpec;
@ -72,6 +75,21 @@ public class InitCommand extends OptionParsingCommand {
*/ */
static class InitOptionHandler extends OptionHandler { static class InitOptionHandler extends OptionHandler {
/**
* Mapping from camelCase options advertised by the service to our kebab-case
* options.
*/
private static final Map<String, String> CAMEL_CASE_OPTIONS;
static {
Map<String, String> options = new HashMap<>();
options.put("--groupId", "--group-id");
options.put("--artifactId", "--artifact-id");
options.put("--packageName", "--package-name");
options.put("--javaVersion", "--java-version");
options.put("--bootVersion", "--boot-version");
CAMEL_CASE_OPTIONS = Collections.unmodifiableMap(options);
}
private final ServiceCapabilitiesReportGenerator serviceCapabilitiesReport; private final ServiceCapabilitiesReportGenerator serviceCapabilitiesReport;
private final ProjectGenerator projectGenerator; private final ProjectGenerator projectGenerator;
@ -113,9 +131,9 @@ public class InitCommand extends OptionParsingCommand {
private OptionSpec<Void> force; private OptionSpec<Void> force;
InitOptionHandler(InitializrService initializrService) { InitOptionHandler(InitializrService initializrService) {
super(InitOptionHandler::processArgument);
this.serviceCapabilitiesReport = new ServiceCapabilitiesReportGenerator(initializrService); this.serviceCapabilitiesReport = new ServiceCapabilitiesReportGenerator(initializrService);
this.projectGenerator = new ProjectGenerator(initializrService); this.projectGenerator = new ProjectGenerator(initializrService);
} }
@Override @Override
@ -129,20 +147,16 @@ public class InitCommand extends OptionParsingCommand {
otherOptions(); otherOptions();
} }
/**
* Supports both kebab-case and camelCase as project CLI Options. camelCase to be
* deprecated as part of future releases
*/
private void projectGenerationOptions() { private void projectGenerationOptions() {
this.groupId = option(Arrays.asList("groupId", "group-id", "g"), this.groupId = option(Arrays.asList("group-id", "g"), "Project coordinates (for example 'org.test')")
"Project coordinates (for example 'org.test')").withRequiredArg(); .withRequiredArg();
this.artifactId = option(Arrays.asList("artifactId", "artifact-id", "a"), this.artifactId = option(Arrays.asList("artifact-id", "a"),
"Project coordinates; infer archive name (for example 'test')").withRequiredArg(); "Project coordinates; infer archive name (for example 'test')").withRequiredArg();
this.version = option(Arrays.asList("version", "v"), "Project version (for example '0.0.1-SNAPSHOT')") this.version = option(Arrays.asList("version", "v"), "Project version (for example '0.0.1-SNAPSHOT')")
.withRequiredArg(); .withRequiredArg();
this.name = option(Arrays.asList("name", "n"), "Project name; infer application name").withRequiredArg(); this.name = option(Arrays.asList("name", "n"), "Project name; infer application name").withRequiredArg();
this.description = option("description", "Project description").withRequiredArg(); this.description = option("description", "Project description").withRequiredArg();
this.packageName = option(Arrays.asList("packageName", "package-name"), "Package name").withRequiredArg(); this.packageName = option(Arrays.asList("package-name"), "Package name").withRequiredArg();
this.type = option(Arrays.asList("type", "t"), this.type = option(Arrays.asList("type", "t"),
"Project type. Not normally needed if you use --build " "Project type. Not normally needed if you use --build "
+ "and/or --format. Check the capabilities of the service (--list) for more details") + "and/or --format. Check the capabilities of the service (--list) for more details")
@ -153,11 +167,11 @@ public class InitCommand extends OptionParsingCommand {
.defaultsTo("maven"); .defaultsTo("maven");
this.format = option("format", "Format of the generated content (for example 'build' for a build file, " this.format = option("format", "Format of the generated content (for example 'build' for a build file, "
+ "'project' for a project archive)").withRequiredArg().defaultsTo("project"); + "'project' for a project archive)").withRequiredArg().defaultsTo("project");
this.javaVersion = option(Arrays.asList("javaVersion", "java-version", "j"), this.javaVersion = option(Arrays.asList("java-version", "j"), "Language level (for example '1.8')")
"Language level (for example '1.8')").withRequiredArg(); .withRequiredArg();
this.language = option(Arrays.asList("language", "l"), "Programming language (for example 'java')") this.language = option(Arrays.asList("language", "l"), "Programming language (for example 'java')")
.withRequiredArg(); .withRequiredArg();
this.bootVersion = option(Arrays.asList("bootVersion", "boot-version", "b"), this.bootVersion = option(Arrays.asList("boot-version", "b"),
"Spring Boot version (for example '1.2.0.RELEASE')").withRequiredArg(); "Spring Boot version (for example '1.2.0.RELEASE')").withRequiredArg();
this.dependencies = option(Arrays.asList("dependencies", "d"), this.dependencies = option(Arrays.asList("dependencies", "d"),
"Comma-separated list of dependency identifiers to include in the generated project") "Comma-separated list of dependency identifiers to include in the generated project")
@ -254,6 +268,16 @@ public class InitCommand extends OptionParsingCommand {
return request; return request;
} }
private static String processArgument(String argument) {
for (Map.Entry<String, String> entry : CAMEL_CASE_OPTIONS.entrySet()) {
String name = entry.getKey();
if (argument.startsWith(name + " ") || argument.startsWith(name + "=")) {
return entry.getValue() + argument.substring(name.length());
}
}
return argument;
}
} }
} }

@ -28,6 +28,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.TreeSet; import java.util.TreeSet;
import java.util.function.Function;
import joptsimple.BuiltinHelpFormatter; import joptsimple.BuiltinHelpFormatter;
import joptsimple.HelpFormatter; import joptsimple.HelpFormatter;
@ -49,12 +50,30 @@ import org.springframework.boot.cli.command.status.ExitStatus;
*/ */
public class OptionHandler { public class OptionHandler {
private final Function<String, String> argumentProcessor;
private OptionParser parser; private OptionParser parser;
private String help; private String help;
private Collection<OptionHelp> optionHelp; private Collection<OptionHelp> optionHelp;
/**
* Create a new {@link OptionHandler} instance.
*/
public OptionHandler() {
this(Function.identity());
}
/**
* Create a new {@link OptionHandler} instance with an argument processor.
* @param argumentProcessor strategy that can be used to manipulate arguments before
* they are used.
*/
public OptionHandler(Function<String, String> argumentProcessor) {
this.argumentProcessor = argumentProcessor;
}
public OptionSpecBuilder option(String name, String description) { public OptionSpecBuilder option(String name, String description) {
return getParser().accepts(name, description); return getParser().accepts(name, description);
} }
@ -80,6 +99,7 @@ public class OptionHandler {
if ("-cp".equals(argsToUse[i])) { if ("-cp".equals(argsToUse[i])) {
argsToUse[i] = "--cp"; argsToUse[i] = "--cp";
} }
argsToUse[i] = this.argumentProcessor.apply(argsToUse[i]);
} }
OptionSet options = getParser().parse(argsToUse); OptionSet options = getParser().parse(argsToUse);
return run(options); return run(options);

@ -274,7 +274,33 @@ class InitCommandTests extends AbstractHttpClientMockTests {
} }
@Test @Test
void parseProjectWithKebabCaseCLIOptions() throws Exception { void parseProjectWithCamelCaseOptions() throws Exception {
this.handler.disableProjectGeneration();
this.command.run("--groupId=org.demo", "--artifactId=acme", "--version=1.2.3-SNAPSHOT", "--name=acme-sample",
"--description=Acme sample project", "--packageName=demo.foo", "--type=ant-project", "--build=grunt",
"--format=web", "--packaging=war", "--javaVersion=1.9", "--language=groovy",
"--bootVersion=1.2.0.RELEASE", "--dependencies=web,data-jpa");
assertThat(this.handler.lastRequest.getGroupId()).isEqualTo("org.demo");
assertThat(this.handler.lastRequest.getArtifactId()).isEqualTo("acme");
assertThat(this.handler.lastRequest.getVersion()).isEqualTo("1.2.3-SNAPSHOT");
assertThat(this.handler.lastRequest.getName()).isEqualTo("acme-sample");
assertThat(this.handler.lastRequest.getDescription()).isEqualTo("Acme sample project");
assertThat(this.handler.lastRequest.getPackageName()).isEqualTo("demo.foo");
assertThat(this.handler.lastRequest.getType()).isEqualTo("ant-project");
assertThat(this.handler.lastRequest.getBuild()).isEqualTo("grunt");
assertThat(this.handler.lastRequest.getFormat()).isEqualTo("web");
assertThat(this.handler.lastRequest.getPackaging()).isEqualTo("war");
assertThat(this.handler.lastRequest.getJavaVersion()).isEqualTo("1.9");
assertThat(this.handler.lastRequest.getLanguage()).isEqualTo("groovy");
assertThat(this.handler.lastRequest.getBootVersion()).isEqualTo("1.2.0.RELEASE");
List<String> dependencies = this.handler.lastRequest.getDependencies();
assertThat(dependencies).hasSize(2);
assertThat(dependencies.contains("web")).isTrue();
assertThat(dependencies.contains("data-jpa")).isTrue();
}
@Test
void parseProjectWithKebabCaseOptions() throws Exception {
this.handler.disableProjectGeneration(); this.handler.disableProjectGeneration();
this.command.run("--group-id=org.demo", "--artifact-id=acme", "--version=1.2.3-SNAPSHOT", "--name=acme-sample", this.command.run("--group-id=org.demo", "--artifact-id=acme", "--version=1.2.3-SNAPSHOT", "--name=acme-sample",
"--description=Acme sample project", "--package-name=demo.foo", "--type=ant-project", "--build=grunt", "--description=Acme sample project", "--package-name=demo.foo", "--type=ant-project", "--build=grunt",

Loading…
Cancel
Save