Skip to content

Commit

Permalink
Polish 'Support both kebab-case and camelCase as Spring init CLI Opti…
Browse files Browse the repository at this point in the history
…ons'

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

See gh-28138
  • Loading branch information
philwebb committed Oct 22, 2021
1 parent ad34732 commit c384fbd
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 13 deletions.
Expand Up @@ -20,7 +20,10 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import joptsimple.OptionSet;
import joptsimple.OptionSpec;
Expand Down Expand Up @@ -72,6 +75,21 @@ public Collection<HelpExample> getExamples() {
*/
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 ProjectGenerator projectGenerator;
Expand Down Expand Up @@ -113,9 +131,9 @@ static class InitOptionHandler extends OptionHandler {
private OptionSpec<Void> force;

InitOptionHandler(InitializrService initializrService) {
super(InitOptionHandler::processArgument);
this.serviceCapabilitiesReport = new ServiceCapabilitiesReportGenerator(initializrService);
this.projectGenerator = new ProjectGenerator(initializrService);

}

@Override
Expand All @@ -129,20 +147,16 @@ protected void options() {
otherOptions();
}

/**
* Supports both kebab-case and camelCase as project CLI Options. camelCase to be
* deprecated as part of future releases
*/
private void projectGenerationOptions() {
this.groupId = option(Arrays.asList("groupId", "group-id", "g"),
"Project coordinates (for example 'org.test')").withRequiredArg();
this.artifactId = option(Arrays.asList("artifactId", "artifact-id", "a"),
this.groupId = option(Arrays.asList("group-id", "g"), "Project coordinates (for example 'org.test')")
.withRequiredArg();
this.artifactId = option(Arrays.asList("artifact-id", "a"),
"Project coordinates; infer archive name (for example 'test')").withRequiredArg();
this.version = option(Arrays.asList("version", "v"), "Project version (for example '0.0.1-SNAPSHOT')")
.withRequiredArg();
this.name = option(Arrays.asList("name", "n"), "Project name; infer application name").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"),
"Project type. Not normally needed if you use --build "
+ "and/or --format. Check the capabilities of the service (--list) for more details")
Expand All @@ -153,11 +167,11 @@ private void projectGenerationOptions() {
.defaultsTo("maven");
this.format = option("format", "Format of the generated content (for example 'build' for a build file, "
+ "'project' for a project archive)").withRequiredArg().defaultsTo("project");
this.javaVersion = option(Arrays.asList("javaVersion", "java-version", "j"),
"Language level (for example '1.8')").withRequiredArg();
this.javaVersion = option(Arrays.asList("java-version", "j"), "Language level (for example '1.8')")
.withRequiredArg();
this.language = option(Arrays.asList("language", "l"), "Programming language (for example 'java')")
.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();
this.dependencies = option(Arrays.asList("dependencies", "d"),
"Comma-separated list of dependency identifiers to include in the generated project")
Expand Down Expand Up @@ -254,6 +268,16 @@ protected ProjectGenerationRequest createProjectGenerationRequest(OptionSet opti
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;
}

}

}
Expand Up @@ -28,6 +28,7 @@
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.function.Function;

import joptsimple.BuiltinHelpFormatter;
import joptsimple.HelpFormatter;
Expand All @@ -49,12 +50,30 @@
*/
public class OptionHandler {

private final Function<String, String> argumentProcessor;

private OptionParser parser;

private String help;

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) {
return getParser().accepts(name, description);
}
Expand All @@ -80,6 +99,7 @@ public final ExitStatus run(String... args) throws Exception {
if ("-cp".equals(argsToUse[i])) {
argsToUse[i] = "--cp";
}
argsToUse[i] = this.argumentProcessor.apply(argsToUse[i]);
}
OptionSet options = getParser().parse(argsToUse);
return run(options);
Expand Down
Expand Up @@ -274,7 +274,33 @@ void parseProjectOptions() throws Exception {
}

@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.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",
Expand Down

0 comments on commit c384fbd

Please sign in to comment.