Add support for global help options
- Essentially this commit registeres on default `--help` and `-h` options to every command and execution short circuits in presense of help options to help command. - Add Supplier<CommandRegistration.Builder> as a bean which can be autowired registration beans. - Make this common bean customisable via CommandRegistrationCustomizer. - Change StandardMethodTargetRegistrar to use supplier so that annotated commands gets common customizations. - Change sample commands to use supplier. - Add new group, spring.shell.help to config props. - Docs changes - Fixes #582 - Fixes #585
This commit is contained in:
@@ -16,19 +16,23 @@
|
||||
package org.springframework.shell.boot;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.shell.MethodTargetRegistrar;
|
||||
import org.springframework.shell.boot.SpringShellProperties.Help;
|
||||
import org.springframework.shell.command.CommandCatalog;
|
||||
import org.springframework.shell.command.CommandCatalogCustomizer;
|
||||
import org.springframework.shell.command.CommandRegistration;
|
||||
import org.springframework.shell.command.CommandResolver;
|
||||
|
||||
@AutoConfiguration
|
||||
@EnableConfigurationProperties(SpringShellProperties.class)
|
||||
public class CommandCatalogAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@@ -55,4 +59,29 @@ public class CommandCatalogAutoConfiguration {
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CommandRegistrationCustomizer helpOptionsCommandRegistrationCustomizer(SpringShellProperties properties) {
|
||||
return registration -> {
|
||||
Help help = properties.getHelp();
|
||||
if (help.isEnabled()) {
|
||||
registration.withHelpOptions()
|
||||
.enabled(true)
|
||||
.longNames(help.getLongNames())
|
||||
.shortNames(help.getShortNames())
|
||||
.command(help.getCommand());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public Supplier<CommandRegistration.Builder> commandRegistrationBuilderSupplier(
|
||||
ObjectProvider<CommandRegistrationCustomizer> customizerProvider) {
|
||||
return () -> {
|
||||
CommandRegistration.Builder builder = CommandRegistration.builder();
|
||||
customizerProvider.orderedStream().forEach((customizer) -> customizer.customize(builder));
|
||||
return builder;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.boot;
|
||||
|
||||
import org.springframework.shell.command.CommandRegistration;
|
||||
|
||||
/**
|
||||
* Callback interface that can be used to customize a {@link CommandRegistration.Builder}.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface CommandRegistrationCustomizer {
|
||||
|
||||
/**
|
||||
* Callback to customize a {@link CommandRegistration.Builder} instance.
|
||||
*
|
||||
* @param commandRegistrationBuilder the command registration builder to customize
|
||||
*/
|
||||
void customize(CommandRegistration.Builder commandRegistrationBuilder);
|
||||
}
|
||||
@@ -32,6 +32,7 @@ public class SpringShellProperties {
|
||||
private Noninteractive noninteractive = new Noninteractive();
|
||||
private Theme theme = new Theme();
|
||||
private Command command = new Command();
|
||||
private Help help = new Help();
|
||||
|
||||
public void setConfig(Config config) {
|
||||
this.config = config;
|
||||
@@ -89,6 +90,14 @@ public class SpringShellProperties {
|
||||
this.command = command;
|
||||
}
|
||||
|
||||
public void setHelp(Help help) {
|
||||
this.help = help;
|
||||
}
|
||||
|
||||
public Help getHelp() {
|
||||
return help;
|
||||
}
|
||||
|
||||
public static class Config {
|
||||
|
||||
private String env;
|
||||
@@ -495,4 +504,59 @@ public class SpringShellProperties {
|
||||
this.showGitCommitTime = showGitCommitTime;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Help {
|
||||
|
||||
/**
|
||||
* Command to call when presense of help option is detected.
|
||||
*/
|
||||
private String command = "help";
|
||||
|
||||
/**
|
||||
* Long style help option, without a prefix "--".
|
||||
*/
|
||||
private String[] longNames = new String[] { "help" };
|
||||
|
||||
/**
|
||||
* Short style help option, without a prefix "-".
|
||||
*/
|
||||
private Character[] shortNames = new Character[] { 'h' };
|
||||
|
||||
/**
|
||||
* Whether to enable help options for commands.
|
||||
*/
|
||||
private boolean enabled = true;
|
||||
|
||||
public String getCommand() {
|
||||
return command;
|
||||
}
|
||||
|
||||
public void setCommand(String command) {
|
||||
this.command = command;
|
||||
}
|
||||
|
||||
public String[] getLongNames() {
|
||||
return longNames;
|
||||
}
|
||||
|
||||
public void setLongNames(String[] longNames) {
|
||||
this.longNames = longNames;
|
||||
}
|
||||
|
||||
public Character[] getShortNames() {
|
||||
return shortNames;
|
||||
}
|
||||
|
||||
public void setShortNames(Character[] shortNames) {
|
||||
this.shortNames = shortNames;
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,10 +16,14 @@
|
||||
|
||||
package org.springframework.shell.boot;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.shell.MethodTargetRegistrar;
|
||||
import org.springframework.shell.command.CommandCatalog;
|
||||
import org.springframework.shell.command.CommandRegistration;
|
||||
import org.springframework.shell.standard.CommandValueProvider;
|
||||
import org.springframework.shell.standard.EnumValueProvider;
|
||||
import org.springframework.shell.standard.FileValueProvider;
|
||||
@@ -50,7 +54,8 @@ public class StandardAPIAutoConfiguration {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public MethodTargetRegistrar standardMethodTargetResolver() {
|
||||
return new StandardMethodTargetRegistrar();
|
||||
public MethodTargetRegistrar standardMethodTargetResolver(ApplicationContext applicationContext,
|
||||
Supplier<CommandRegistration.Builder> builder) {
|
||||
return new StandardMethodTargetRegistrar(applicationContext, builder);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,6 +63,10 @@ public class SpringShellPropertiesTests {
|
||||
assertThat(properties.getCommand().getVersion().isShowGitCommitId()).isFalse();
|
||||
assertThat(properties.getCommand().getVersion().isShowGitShortCommitId()).isFalse();
|
||||
assertThat(properties.getCommand().getVersion().isShowGitCommitTime()).isFalse();
|
||||
assertThat(properties.getHelp().isEnabled()).isTrue();
|
||||
assertThat(properties.getHelp().getCommand()).isEqualTo("help");
|
||||
assertThat(properties.getHelp().getLongNames()).containsExactly("help");
|
||||
assertThat(properties.getHelp().getShortNames()).containsExactly('h');
|
||||
});
|
||||
}
|
||||
|
||||
@@ -99,6 +103,10 @@ public class SpringShellPropertiesTests {
|
||||
.withPropertyValues("spring.shell.command.version.show-git-commit-id=true")
|
||||
.withPropertyValues("spring.shell.command.version.show-git-short-commit-id=true")
|
||||
.withPropertyValues("spring.shell.command.version.show-git-commit-time=true")
|
||||
.withPropertyValues("spring.shell.help.enabled=false")
|
||||
.withPropertyValues("spring.shell.help.command=fake")
|
||||
.withPropertyValues("spring.shell.help.long-names=fake")
|
||||
.withPropertyValues("spring.shell.help.short-names=f")
|
||||
.withUserConfiguration(Config1.class)
|
||||
.run((context) -> {
|
||||
SpringShellProperties properties = context.getBean(SpringShellProperties.class);
|
||||
@@ -132,6 +140,24 @@ public class SpringShellPropertiesTests {
|
||||
assertThat(properties.getCommand().getVersion().isShowGitCommitId()).isTrue();
|
||||
assertThat(properties.getCommand().getVersion().isShowGitShortCommitId()).isTrue();
|
||||
assertThat(properties.getCommand().getVersion().isShowGitCommitTime()).isTrue();
|
||||
assertThat(properties.getHelp().isEnabled()).isFalse();
|
||||
assertThat(properties.getHelp().getCommand()).isEqualTo("fake");
|
||||
assertThat(properties.getHelp().getLongNames()).containsExactly("fake");
|
||||
assertThat(properties.getHelp().getShortNames()).containsExactly('f');
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void essentiallyUnset() {
|
||||
this.contextRunner
|
||||
.withPropertyValues("spring.shell.help.long-names=")
|
||||
.withPropertyValues("spring.shell.help.short-names=")
|
||||
.withUserConfiguration(Config1.class)
|
||||
.run((context) -> {
|
||||
SpringShellProperties properties = context.getBean(SpringShellProperties.class);
|
||||
assertThat(properties.getHelp().getLongNames()).isEmpty();
|
||||
assertThat(properties.getHelp().getShortNames()).isEmpty();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -236,7 +236,7 @@ public class Shell {
|
||||
|
||||
CommandExecution execution = CommandExecution.of(
|
||||
argumentResolvers != null ? argumentResolvers.getResolvers() : null, validator, terminal,
|
||||
conversionService);
|
||||
conversionService, commandRegistry);
|
||||
|
||||
List<CommandExceptionResolver> commandExceptionResolvers = commandRegistration.get().getExceptionResolvers();
|
||||
|
||||
|
||||
@@ -15,12 +15,12 @@
|
||||
*/
|
||||
package org.springframework.shell.command;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import jakarta.validation.Validator;
|
||||
|
||||
import org.jline.terminal.Terminal;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
@@ -33,10 +33,12 @@ import org.springframework.shell.Availability;
|
||||
import org.springframework.shell.CommandNotCurrentlyAvailable;
|
||||
import org.springframework.shell.command.CommandParser.CommandParserException;
|
||||
import org.springframework.shell.command.CommandParser.CommandParserResults;
|
||||
import org.springframework.shell.command.CommandRegistration.HelpOptionInfo;
|
||||
import org.springframework.shell.command.CommandRegistration.TargetInfo;
|
||||
import org.springframework.shell.command.CommandRegistration.TargetInfo.TargetType;
|
||||
import org.springframework.shell.command.invocation.InvocableShellMethod;
|
||||
import org.springframework.shell.command.invocation.ShellMethodArgumentResolverComposite;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* Interface to evaluate a result from a command with an arguments.
|
||||
@@ -61,7 +63,7 @@ public interface CommandExecution {
|
||||
* @return default command execution
|
||||
*/
|
||||
public static CommandExecution of(List<? extends HandlerMethodArgumentResolver> resolvers) {
|
||||
return new DefaultCommandExecution(resolvers, null, null, null);
|
||||
return new DefaultCommandExecution(resolvers, null, null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -75,7 +77,21 @@ public interface CommandExecution {
|
||||
*/
|
||||
public static CommandExecution of(List<? extends HandlerMethodArgumentResolver> resolvers, Validator validator,
|
||||
Terminal terminal, ConversionService conversionService) {
|
||||
return new DefaultCommandExecution(resolvers, validator, terminal, conversionService);
|
||||
return new DefaultCommandExecution(resolvers, validator, terminal, conversionService, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an instance of a default {@link CommandExecution}.
|
||||
*
|
||||
* @param resolvers the handler method argument resolvers
|
||||
* @param validator the validator
|
||||
* @param terminal the terminal
|
||||
* @param conversionService the conversion services
|
||||
* @return default command execution
|
||||
*/
|
||||
public static CommandExecution of(List<? extends HandlerMethodArgumentResolver> resolvers, Validator validator,
|
||||
Terminal terminal, ConversionService conversionService, CommandCatalog commandCatalog) {
|
||||
return new DefaultCommandExecution(resolvers, validator, terminal, conversionService, commandCatalog);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -87,13 +103,15 @@ public interface CommandExecution {
|
||||
private Validator validator;
|
||||
private Terminal terminal;
|
||||
private ConversionService conversionService;
|
||||
private CommandCatalog commandCatalog;
|
||||
|
||||
public DefaultCommandExecution(List<? extends HandlerMethodArgumentResolver> resolvers, Validator validator,
|
||||
Terminal terminal, ConversionService conversionService) {
|
||||
Terminal terminal, ConversionService conversionService, CommandCatalog commandCatalog) {
|
||||
this.resolvers = resolvers;
|
||||
this.validator = validator;
|
||||
this.terminal = terminal;
|
||||
this.conversionService = conversionService;
|
||||
this.commandCatalog = commandCatalog;
|
||||
}
|
||||
|
||||
public Object evaluate(CommandRegistration registration, String[] args) {
|
||||
@@ -107,15 +125,59 @@ public interface CommandExecution {
|
||||
CommandParser parser = CommandParser.of(conversionService);
|
||||
CommandParserResults results = parser.parse(options, args);
|
||||
|
||||
// check help options to short circuit
|
||||
boolean handleHelpOption = false;
|
||||
HelpOptionInfo helpOption = registration.getHelpOption();
|
||||
if (helpOption.isEnabled() && helpOption.getCommand() != null && (!ObjectUtils.isEmpty(helpOption.getLongNames()) || !ObjectUtils.isEmpty(helpOption.getShortNames()))) {
|
||||
handleHelpOption = results.results().stream()
|
||||
.filter(cpr -> {
|
||||
boolean present = false;
|
||||
if (helpOption.getLongNames() != null) {
|
||||
present = Arrays.asList(cpr.option().getLongNames()).stream()
|
||||
.filter(ln -> ObjectUtils.containsElement(helpOption.getLongNames(), ln))
|
||||
.findFirst()
|
||||
.isPresent();
|
||||
}
|
||||
if (present) {
|
||||
return true;
|
||||
}
|
||||
if (helpOption.getShortNames() != null) {
|
||||
present = Arrays.asList(cpr.option().getShortNames()).stream()
|
||||
.filter(sn -> ObjectUtils.containsElement(helpOption.getShortNames(), sn))
|
||||
.findFirst()
|
||||
.isPresent();
|
||||
}
|
||||
return present;
|
||||
})
|
||||
.findFirst()
|
||||
.isPresent();
|
||||
}
|
||||
|
||||
// if needed switch registration to help command if we're short circuiting
|
||||
CommandRegistration usedRegistration;
|
||||
if (handleHelpOption) {
|
||||
String command = registration.getCommand();
|
||||
CommandParser helpParser = CommandParser.of(conversionService);
|
||||
CommandRegistration helpCommandRegistration = commandCatalog.getRegistrations()
|
||||
.get(registration.getHelpOption().getCommand());
|
||||
List<CommandOption> helpOptions = helpCommandRegistration.getOptions();
|
||||
CommandParserResults helpResults = helpParser.parse(helpOptions, new String[] { "--command", command });
|
||||
results = helpResults;
|
||||
usedRegistration = helpCommandRegistration;
|
||||
}
|
||||
else {
|
||||
usedRegistration = registration;
|
||||
}
|
||||
|
||||
if (!results.errors().isEmpty()) {
|
||||
throw new CommandParserExceptionsException("Command parser resulted errors", results.errors());
|
||||
}
|
||||
|
||||
CommandContext ctx = CommandContext.of(args, results, terminal, registration);
|
||||
CommandContext ctx = CommandContext.of(args, results, terminal, usedRegistration);
|
||||
|
||||
Object res = null;
|
||||
|
||||
TargetInfo targetInfo = registration.getTarget();
|
||||
TargetInfo targetInfo = usedRegistration.getTarget();
|
||||
|
||||
// pick the target to execute
|
||||
if (targetInfo.getTargetType() == TargetType.FUNCTION) {
|
||||
|
||||
@@ -121,6 +121,13 @@ public interface CommandRegistration {
|
||||
*/
|
||||
List<CommandExceptionResolver> getExceptionResolvers();
|
||||
|
||||
/**
|
||||
* Gets a help option info.
|
||||
*
|
||||
* @return the help option info
|
||||
*/
|
||||
HelpOptionInfo getHelpOption();
|
||||
|
||||
/**
|
||||
* Gets a new instance of a {@link Builder}.
|
||||
*
|
||||
@@ -475,6 +482,125 @@ public interface CommandRegistration {
|
||||
Builder and();
|
||||
}
|
||||
|
||||
public interface HelpOptionInfo {
|
||||
|
||||
/**
|
||||
* Gets whether help options are enabled.
|
||||
*
|
||||
* @return whether help options are enabled
|
||||
*/
|
||||
boolean isEnabled();
|
||||
|
||||
/**
|
||||
* Gets long names options for help.
|
||||
*
|
||||
* @return long names options for help
|
||||
*/
|
||||
String[] getLongNames();
|
||||
|
||||
/**
|
||||
* Gets short names options for help.
|
||||
*
|
||||
* @return short names options for help
|
||||
*/
|
||||
Character[] getShortNames();
|
||||
|
||||
/**
|
||||
* Gets command for help.
|
||||
*
|
||||
* @return command for help
|
||||
*/
|
||||
String getCommand();
|
||||
|
||||
static HelpOptionInfo of() {
|
||||
return of(false, null, null, null);
|
||||
}
|
||||
|
||||
static HelpOptionInfo of(boolean enabled, String[] longNames, Character[] shortNames, String command) {
|
||||
return new DefaultHelpOptionInfo(enabled, longNames, shortNames, command);
|
||||
}
|
||||
|
||||
static class DefaultHelpOptionInfo implements HelpOptionInfo {
|
||||
|
||||
private final String command;
|
||||
private final String[] longNames;
|
||||
private final Character[] shortNames;
|
||||
private final boolean enabled;
|
||||
|
||||
public DefaultHelpOptionInfo(boolean enabled, String[] longNames, Character[] shortNames, String command) {
|
||||
this.command = command;
|
||||
this.longNames = longNames;
|
||||
this.shortNames = shortNames;
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getLongNames() {
|
||||
return longNames;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Character[] getShortNames() {
|
||||
return shortNames;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCommand() {
|
||||
return command;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Spec defining help options.
|
||||
*/
|
||||
public interface HelpOptionsSpec {
|
||||
|
||||
/**
|
||||
* Whether help options are enabled.
|
||||
*
|
||||
* @param enabled the enabled flag
|
||||
* @return a help option for chaining
|
||||
*/
|
||||
HelpOptionsSpec enabled(boolean enabled);
|
||||
|
||||
/**
|
||||
* Sets long names options for help.
|
||||
*
|
||||
* @param longNames the long names
|
||||
* @return a help option for chaining
|
||||
*/
|
||||
HelpOptionsSpec longNames(String... longNames);
|
||||
|
||||
/**
|
||||
* Sets short names options for help.
|
||||
*
|
||||
* @param shortNames the short names
|
||||
* @return a help option for chaining
|
||||
*/
|
||||
HelpOptionsSpec shortNames(Character... shortNames);
|
||||
|
||||
/**
|
||||
* Sets command used for help.
|
||||
*
|
||||
* @param command the command
|
||||
* @return a help option for chaining
|
||||
*/
|
||||
HelpOptionsSpec command(String command);
|
||||
|
||||
/**
|
||||
* Return a builder for chaining.
|
||||
*
|
||||
* @return a builder for chaining
|
||||
*/
|
||||
Builder and();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder interface for {@link CommandRegistration}.
|
||||
*/
|
||||
@@ -575,6 +701,13 @@ public interface CommandRegistration {
|
||||
*/
|
||||
ErrorHandlingSpec withErrorHandling();
|
||||
|
||||
/**
|
||||
* Define help options what this command should use.
|
||||
*
|
||||
* @return help options spec for chaining
|
||||
*/
|
||||
HelpOptionsSpec withHelpOptions();
|
||||
|
||||
/**
|
||||
* Builds a {@link CommandRegistration}.
|
||||
*
|
||||
@@ -884,6 +1017,57 @@ public interface CommandRegistration {
|
||||
}
|
||||
}
|
||||
|
||||
static class DefaultHelpOptionsSpec implements HelpOptionsSpec {
|
||||
|
||||
private BaseBuilder builder;
|
||||
private String command;
|
||||
private String[] longNames;
|
||||
private Character[] shortNames;
|
||||
private boolean enabled = true;
|
||||
|
||||
DefaultHelpOptionsSpec(BaseBuilder builder) {
|
||||
this.builder = builder;
|
||||
}
|
||||
|
||||
DefaultHelpOptionsSpec(BaseBuilder otherBuilder, DefaultHelpOptionsSpec otherSpec) {
|
||||
this.builder = otherBuilder;
|
||||
this.builder.helpOptionsSpec = this;
|
||||
this.command = otherSpec.command;
|
||||
this.longNames = otherSpec.longNames.clone();
|
||||
this.shortNames = otherSpec.shortNames.clone();
|
||||
this.enabled = otherSpec.enabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HelpOptionsSpec command(String command) {
|
||||
this.command = command;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HelpOptionsSpec longNames(String... longNames) {
|
||||
this.longNames = longNames;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HelpOptionsSpec shortNames(Character... shortNames) {
|
||||
this.shortNames = shortNames;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HelpOptionsSpec enabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder and() {
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
|
||||
static class DefaultCommandRegistration implements CommandRegistration {
|
||||
|
||||
private String command;
|
||||
@@ -897,11 +1081,12 @@ public interface CommandRegistration {
|
||||
private List<DefaultAliasSpec> aliasSpecs;
|
||||
private DefaultExitCodeSpec exitCodeSpec;
|
||||
private DefaultErrorHandlingSpec errorHandlingSpec;
|
||||
private DefaultHelpOptionsSpec helpOptionsSpec;
|
||||
|
||||
public DefaultCommandRegistration(String[] commands, InteractionMode interactionMode, String group,
|
||||
boolean hidden, String description, Supplier<Availability> availability,
|
||||
List<DefaultOptionSpec> optionSpecs, DefaultTargetSpec targetSpec, List<DefaultAliasSpec> aliasSpecs,
|
||||
DefaultExitCodeSpec exitCodeSpec, DefaultErrorHandlingSpec errorHandlingSpec) {
|
||||
DefaultExitCodeSpec exitCodeSpec, DefaultErrorHandlingSpec errorHandlingSpec, DefaultHelpOptionsSpec helpOptionsSpec) {
|
||||
this.command = commandArrayToName(commands);
|
||||
this.interactionMode = interactionMode;
|
||||
this.group = group;
|
||||
@@ -913,6 +1098,7 @@ public interface CommandRegistration {
|
||||
this.aliasSpecs = aliasSpecs;
|
||||
this.exitCodeSpec = exitCodeSpec;
|
||||
this.errorHandlingSpec = errorHandlingSpec;
|
||||
this.helpOptionsSpec = helpOptionsSpec;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -947,11 +1133,17 @@ public interface CommandRegistration {
|
||||
|
||||
@Override
|
||||
public List<CommandOption> getOptions() {
|
||||
return optionSpecs.stream()
|
||||
List<CommandOption> options = optionSpecs.stream()
|
||||
.map(o -> CommandOption.of(o.getLongNames(), o.getShortNames(), o.getDescription(), o.getType(),
|
||||
o.isRequired(), o.getDefaultValue(), o.getPosition(), o.getArityMin(), o.getArityMax(),
|
||||
o.getLabel(), o.getCompletion()))
|
||||
.collect(Collectors.toList());
|
||||
if (helpOptionsSpec != null) {
|
||||
String[] longNames = helpOptionsSpec.longNames != null ? helpOptionsSpec.longNames : null;
|
||||
Character[] shortNames = helpOptionsSpec.shortNames != null ? helpOptionsSpec.shortNames : null;
|
||||
options.add(CommandOption.of(longNames, shortNames, "help for " + command));
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -997,6 +1189,17 @@ public interface CommandRegistration {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public HelpOptionInfo getHelpOption() {
|
||||
if (this.helpOptionsSpec == null) {
|
||||
return HelpOptionInfo.of();
|
||||
}
|
||||
else {
|
||||
return HelpOptionInfo.of(helpOptionsSpec.enabled, helpOptionsSpec.longNames, helpOptionsSpec.shortNames,
|
||||
helpOptionsSpec.command);
|
||||
}
|
||||
}
|
||||
|
||||
private static String commandArrayToName(String[] commands) {
|
||||
return Arrays.asList(commands).stream()
|
||||
.flatMap(c -> Stream.of(c.split(" ")))
|
||||
@@ -1007,10 +1210,9 @@ public interface CommandRegistration {
|
||||
}
|
||||
|
||||
static class DefaultBuilder extends BaseBuilder {
|
||||
|
||||
}
|
||||
|
||||
static class BaseBuilder implements Builder {
|
||||
static abstract class BaseBuilder implements Builder {
|
||||
|
||||
private String[] commands;
|
||||
private InteractionMode interactionMode = InteractionMode.ALL;
|
||||
@@ -1023,6 +1225,7 @@ public interface CommandRegistration {
|
||||
private DefaultTargetSpec targetSpec;
|
||||
private DefaultExitCodeSpec exitCodeSpec;
|
||||
private DefaultErrorHandlingSpec errorHandlingSpec;
|
||||
private DefaultHelpOptionsSpec helpOptionsSpec;
|
||||
|
||||
@Override
|
||||
public Builder command(String... commands) {
|
||||
@@ -1106,13 +1309,21 @@ public interface CommandRegistration {
|
||||
return spec;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HelpOptionsSpec withHelpOptions() {
|
||||
if (this.helpOptionsSpec == null) {
|
||||
this.helpOptionsSpec = new DefaultHelpOptionsSpec(this);
|
||||
}
|
||||
return this.helpOptionsSpec;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandRegistration build() {
|
||||
Assert.notNull(commands, "command cannot be empty");
|
||||
Assert.notNull(targetSpec, "target cannot be empty");
|
||||
Assert.state(!(targetSpec.bean != null && targetSpec.function != null), "only one target can exist");
|
||||
return new DefaultCommandRegistration(commands, interactionMode, group, hidden, description, availability,
|
||||
optionSpecs, targetSpec, aliasSpecs, exitCodeSpec, errorHandlingSpec);
|
||||
optionSpecs, targetSpec, aliasSpecs, exitCodeSpec, errorHandlingSpec, helpOptionsSpec);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -523,4 +523,24 @@ public class CommandRegistrationTests extends AbstractCommandTests {
|
||||
.build();
|
||||
assertThat(registration.isHidden()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHelpOption() {
|
||||
CommandRegistration registration = CommandRegistration.builder()
|
||||
.command("command1")
|
||||
.withHelpOptions()
|
||||
.enabled(true)
|
||||
.longNames(new String[] { "help" })
|
||||
.shortNames(new Character[] { 'h' })
|
||||
.command("help")
|
||||
.and()
|
||||
.withTarget()
|
||||
.function(function1)
|
||||
.and()
|
||||
.build();
|
||||
|
||||
assertThat(registration.getOptions()).hasSize(1);
|
||||
assertThat(registration.getOptions().get(0).getLongNames()).containsExactly("help");
|
||||
assertThat(registration.getOptions().get(0).getShortNames()).containsExactly('h');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
[[commands-helpoptions]]
|
||||
=== Help Options
|
||||
ifndef::snippets[:snippets: ../../test/java/org/springframework/shell/docs]
|
||||
|
||||
_Spring Shell_ has a build-in `help` command but not all favour getting command help
|
||||
from it as you always need to call it with arguments for target command. It's
|
||||
common in many cli frameworks for every command having options _--help_ and _-h_
|
||||
to print out command help.
|
||||
|
||||
Default functionality is that every command will get modified to have options
|
||||
_--help_ and _-h_, which if present in a given command will automatically
|
||||
short circuit command execution into a existing `help` command regardless
|
||||
what other command-line options is typed.
|
||||
|
||||
Below example shows its default settings.
|
||||
|
||||
====
|
||||
[source, java, indent=0]
|
||||
----
|
||||
include::{snippets}/CommandRegistrationHelpOptionsSnippets.java[tag=defaults]
|
||||
----
|
||||
====
|
||||
|
||||
It is possible to change default behaviour via configuration options.
|
||||
|
||||
====
|
||||
[source, yaml]
|
||||
----
|
||||
spring:
|
||||
shell:
|
||||
help:
|
||||
enabled: true
|
||||
long-names: help
|
||||
short-names: h
|
||||
command: help
|
||||
----
|
||||
====
|
||||
|
||||
NOTE: Commands defined programmationally or via annotations will automatically add
|
||||
help options. With annotation model you can only turn things off globally, programmatic
|
||||
model gives option to modify settings per command.
|
||||
@@ -1,11 +1,40 @@
|
||||
==== Programmatic Model
|
||||
ifndef::snippets[:snippets: ../../test/java/org/springframework/shell/docs]
|
||||
|
||||
In the programmatic model, `CommandRegistration` is defined as a `@Bean`, and it is automatically registered:
|
||||
In the programmatic model, `CommandRegistration` can be defined as a `@Bean`
|
||||
and it will be automatically registered.
|
||||
|
||||
====
|
||||
[source, java, indent=0]
|
||||
----
|
||||
include::{snippets}/CommandRegistrationBeanSnippets.java[tag=snippet1]
|
||||
include::{snippets}/CommandRegistrationBeanSnippets.java[tag=plain]
|
||||
----
|
||||
====
|
||||
|
||||
If all your commands have something in common, an instance of
|
||||
a _Supplier<CommandRegistration.Builder>_ is created which can
|
||||
be autowired. Default implementation of this supplier returns
|
||||
a new builder so you don't need to worry about its internal state.
|
||||
|
||||
IMPORTANT: Commands registered programmatically automatically
|
||||
add _help options_ mentioned in <<commands-helpoptions>>.
|
||||
|
||||
If bean of this supplier type is defined then auto-configuration
|
||||
will back off giving you an option to redefine default functionality.
|
||||
|
||||
====
|
||||
[source, java, indent=0]
|
||||
----
|
||||
include::{snippets}/CommandRegistrationBeanSnippets.java[tag=fromsupplier]
|
||||
----
|
||||
====
|
||||
|
||||
`CommandRegistrationCustomizer` beans can be defined if you want to centrally
|
||||
modify builder instance given you by supplier mentioned above.
|
||||
|
||||
====
|
||||
[source, java, indent=0]
|
||||
----
|
||||
include::{snippets}/CommandRegistrationBeanSnippets.java[tag=customizer]
|
||||
----
|
||||
====
|
||||
|
||||
@@ -15,6 +15,8 @@ include::using-shell-commands-exceptionhandling.adoc[]
|
||||
|
||||
include::using-shell-commands-hidden.adoc[]
|
||||
|
||||
include::using-shell-commands-helpoptions.adoc[]
|
||||
|
||||
include::using-shell-commands-interactionmode.adoc[]
|
||||
|
||||
include::using-shell-commands-builtin.adoc[]
|
||||
|
||||
@@ -15,17 +15,44 @@
|
||||
*/
|
||||
package org.springframework.shell.docs;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.shell.boot.CommandRegistrationCustomizer;
|
||||
import org.springframework.shell.command.CommandRegistration;
|
||||
|
||||
public class CommandRegistrationBeanSnippets {
|
||||
|
||||
// tag::snippet1[]
|
||||
@Bean
|
||||
CommandRegistration commandRegistration() {
|
||||
return CommandRegistration.builder()
|
||||
.command("mycommand")
|
||||
.build();
|
||||
class Dump1 {
|
||||
// tag::plain[]
|
||||
@Bean
|
||||
CommandRegistration commandRegistration() {
|
||||
return CommandRegistration.builder()
|
||||
.command("mycommand")
|
||||
.build();
|
||||
}
|
||||
// end::plain[]
|
||||
}
|
||||
|
||||
class Dump2 {
|
||||
// tag::fromsupplier[]
|
||||
@Bean
|
||||
CommandRegistration commandRegistration(Supplier<CommandRegistration.Builder> builder) {
|
||||
return builder.get()
|
||||
.command("mycommand")
|
||||
.build();
|
||||
}
|
||||
// end::fromsupplier[]
|
||||
}
|
||||
|
||||
class Dump3 {
|
||||
// tag::customizer[]
|
||||
@Bean
|
||||
CommandRegistrationCustomizer commandRegistrationCustomizerExample() {
|
||||
return builder -> {
|
||||
// customize instance of CommandRegistration.Builder
|
||||
};
|
||||
}
|
||||
// end::customizer[]
|
||||
}
|
||||
// end::snippet1[]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.docs;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.shell.command.CommandRegistration;
|
||||
|
||||
class CommandRegistrationHelpOptionsSnippets {
|
||||
|
||||
class Dump1 {
|
||||
// tag::defaults[]
|
||||
@Bean
|
||||
CommandRegistration commandRegistration() {
|
||||
return CommandRegistration.builder()
|
||||
.command("mycommand")
|
||||
.withHelpOptions()
|
||||
.enabled(true)
|
||||
.longNames("help")
|
||||
.shortNames('h')
|
||||
.command("help")
|
||||
.and()
|
||||
.build();
|
||||
}
|
||||
// end::defaults[]
|
||||
}
|
||||
|
||||
}
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package org.springframework.shell.samples.e2e;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.shell.command.CommandRegistration;
|
||||
import org.springframework.shell.command.CommandRegistration.OptionArity;
|
||||
@@ -38,8 +40,8 @@ public class ArityCommands extends BaseE2ECommands {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CommandRegistration testBooleanArity1DefaultTrueRegistration() {
|
||||
return CommandRegistration.builder()
|
||||
public CommandRegistration testBooleanArity1DefaultTrueRegistration(Supplier<CommandRegistration.Builder> builder) {
|
||||
return builder.get()
|
||||
.command(REG, "boolean-arity1-default-true")
|
||||
.group(GROUP)
|
||||
.withOption()
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package org.springframework.shell.samples.e2e;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.shell.command.CommandRegistration;
|
||||
import org.springframework.shell.standard.ShellComponent;
|
||||
@@ -37,8 +39,8 @@ public class DefaultValueCommands extends BaseE2ECommands {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CommandRegistration testDefaultValueRegistration() {
|
||||
return CommandRegistration.builder()
|
||||
public CommandRegistration testDefaultValueRegistration(Supplier<CommandRegistration.Builder> builder) {
|
||||
return builder.get()
|
||||
.command(REG, "default-value")
|
||||
.group(GROUP)
|
||||
.withOption()
|
||||
@@ -62,8 +64,8 @@ public class DefaultValueCommands extends BaseE2ECommands {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CommandRegistration testDefaultValueBoolean1Registration() {
|
||||
return CommandRegistration.builder()
|
||||
public CommandRegistration testDefaultValueBoolean1Registration(Supplier<CommandRegistration.Builder> builder) {
|
||||
return builder.get()
|
||||
.command(REG, "default-value-boolean1")
|
||||
.group(GROUP)
|
||||
.withOption()
|
||||
@@ -88,8 +90,8 @@ public class DefaultValueCommands extends BaseE2ECommands {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CommandRegistration testDefaultValueBoolean2Registration() {
|
||||
return CommandRegistration.builder()
|
||||
public CommandRegistration testDefaultValueBoolean2Registration(Supplier<CommandRegistration.Builder> builder) {
|
||||
return builder.get()
|
||||
.command(REG, "default-value-boolean2")
|
||||
.group(GROUP)
|
||||
.withOption()
|
||||
@@ -114,8 +116,8 @@ public class DefaultValueCommands extends BaseE2ECommands {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CommandRegistration testDefaultValueBoolean3Registration() {
|
||||
return CommandRegistration.builder()
|
||||
public CommandRegistration testDefaultValueBoolean3Registration(Supplier<CommandRegistration.Builder> builder) {
|
||||
return builder.get()
|
||||
.command(REG, "default-value-boolean3")
|
||||
.group(GROUP)
|
||||
.withOption()
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package org.springframework.shell.samples.e2e;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.boot.ExitCodeGenerator;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.shell.command.CommandRegistration;
|
||||
@@ -31,8 +33,8 @@ import org.springframework.shell.standard.ShellComponent;
|
||||
public class ErrorHandlingCommands extends BaseE2ECommands {
|
||||
|
||||
@Bean
|
||||
public CommandRegistration testErrorHandlingRegistration() {
|
||||
return CommandRegistration.builder()
|
||||
public CommandRegistration testErrorHandlingRegistration(Supplier<CommandRegistration.Builder> builder) {
|
||||
return builder.get()
|
||||
.command(REG, "error-handling")
|
||||
.group(GROUP)
|
||||
.withOption()
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package org.springframework.shell.samples.e2e;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.shell.command.CommandRegistration;
|
||||
import org.springframework.shell.standard.ShellComponent;
|
||||
@@ -28,8 +30,8 @@ import org.springframework.shell.standard.ShellComponent;
|
||||
public class ExitCodeCommands extends BaseE2ECommands {
|
||||
|
||||
@Bean
|
||||
public CommandRegistration testExitCodeRegistration() {
|
||||
return CommandRegistration.builder()
|
||||
public CommandRegistration testExitCodeRegistration(Supplier<CommandRegistration.Builder> builder) {
|
||||
return builder.get()
|
||||
.command(REG, "exit-code")
|
||||
.group(GROUP)
|
||||
.withOption()
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright 2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.samples.e2e;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.shell.command.CommandRegistration;
|
||||
import org.springframework.shell.standard.ShellComponent;
|
||||
import org.springframework.shell.standard.ShellMethod;
|
||||
import org.springframework.shell.standard.ShellOption;
|
||||
|
||||
@ShellComponent
|
||||
public class HelpOptionCommands extends BaseE2ECommands {
|
||||
|
||||
@Autowired
|
||||
Supplier<CommandRegistration.Builder> builder;
|
||||
|
||||
@ShellMethod(key = LEGACY_ANNO + "help-option-default", group = GROUP)
|
||||
public String testHelpOptionDefault(
|
||||
@ShellOption(defaultValue = "hi") String arg1
|
||||
) {
|
||||
return "Hello " + arg1;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CommandRegistration testHelpOptionDefaultRegistration() {
|
||||
return builder.get()
|
||||
.command(REG, "help-option-default")
|
||||
.group(GROUP)
|
||||
.withOption()
|
||||
.longNames("arg1")
|
||||
.defaultValue("hi")
|
||||
.and()
|
||||
.withTarget()
|
||||
.function(ctx -> {
|
||||
String arg1 = ctx.getOptionValue("arg1");
|
||||
return "Hello " + arg1;
|
||||
})
|
||||
.and()
|
||||
.build();
|
||||
}
|
||||
|
||||
// @ShellMethod(key = LEGACY_ANNO + "help-option-exists", group = GROUP)
|
||||
// public String testHelpOptionExists(
|
||||
// @ShellOption(defaultValue = "hi") String help
|
||||
// ) {
|
||||
// return "Hello " + help;
|
||||
// }
|
||||
|
||||
@Bean
|
||||
public CommandRegistration testHelpOptionExistsRegistration() {
|
||||
return builder.get()
|
||||
.command(REG, "help-option-exists")
|
||||
.group(GROUP)
|
||||
.withOption()
|
||||
.longNames("help")
|
||||
.defaultValue("hi")
|
||||
.and()
|
||||
.withHelpOptions()
|
||||
.longNames("myhelp")
|
||||
.and()
|
||||
.withTarget()
|
||||
.function(ctx -> {
|
||||
String arg1 = ctx.getOptionValue("help");
|
||||
return "Hello " + arg1;
|
||||
})
|
||||
.and()
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package org.springframework.shell.samples.e2e;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.shell.command.CommandRegistration;
|
||||
import org.springframework.shell.standard.ShellComponent;
|
||||
@@ -23,8 +25,8 @@ import org.springframework.shell.standard.ShellComponent;
|
||||
public class HiddenCommands extends BaseE2ECommands {
|
||||
|
||||
@Bean
|
||||
public CommandRegistration testHidden1Registration() {
|
||||
return CommandRegistration.builder()
|
||||
public CommandRegistration testHidden1Registration(Supplier<CommandRegistration.Builder> builder) {
|
||||
return builder.get()
|
||||
.command(REG, "hidden-1")
|
||||
.group(GROUP)
|
||||
.hidden()
|
||||
|
||||
@@ -17,6 +17,7 @@ package org.springframework.shell.samples.e2e;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -40,10 +41,10 @@ public class InteractiveCompletionCommands extends BaseE2ECommands {
|
||||
}
|
||||
|
||||
@Bean
|
||||
CommandRegistration testInteractiveCompletion1Registration() {
|
||||
CommandRegistration testInteractiveCompletion1Registration(Supplier<CommandRegistration.Builder> builder) {
|
||||
Test1ValuesProvider test1ValuesProvider = new Test1ValuesProvider();
|
||||
Test2ValuesProvider test2ValuesProvider = new Test2ValuesProvider();
|
||||
return CommandRegistration.builder()
|
||||
return builder.get()
|
||||
.command(REG, "interactive-completion-1")
|
||||
.group(GROUP)
|
||||
.withOption()
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
package org.springframework.shell.samples.e2e;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.shell.command.CommandRegistration;
|
||||
@@ -32,8 +33,8 @@ import org.springframework.shell.standard.ShellOption;
|
||||
public class OptionTypeCommands extends BaseE2ECommands {
|
||||
|
||||
@Bean
|
||||
public CommandRegistration testOptionTypeRegistration() {
|
||||
return CommandRegistration.builder()
|
||||
public CommandRegistration testOptionTypeRegistration(Supplier<CommandRegistration.Builder> builder) {
|
||||
return builder.get()
|
||||
.command(REG, "option-type")
|
||||
.group(GROUP)
|
||||
.withOption()
|
||||
@@ -76,8 +77,8 @@ public class OptionTypeCommands extends BaseE2ECommands {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CommandRegistration optionTypeStringRegistration() {
|
||||
return CommandRegistration.builder()
|
||||
public CommandRegistration optionTypeStringRegistration(Supplier<CommandRegistration.Builder> builder) {
|
||||
return builder.get()
|
||||
.command(REG, "option-type-string")
|
||||
.group(GROUP)
|
||||
.withOption()
|
||||
@@ -112,8 +113,8 @@ public class OptionTypeCommands extends BaseE2ECommands {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CommandRegistration optionTypeBooleanRegistration() {
|
||||
return CommandRegistration.builder()
|
||||
public CommandRegistration optionTypeBooleanRegistration(Supplier<CommandRegistration.Builder> builder) {
|
||||
return builder.get()
|
||||
.command(REG, "option-type-boolean")
|
||||
.group(GROUP)
|
||||
.withOption()
|
||||
@@ -172,8 +173,8 @@ public class OptionTypeCommands extends BaseE2ECommands {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CommandRegistration optionTypeIntegerRegistration() {
|
||||
return CommandRegistration.builder()
|
||||
public CommandRegistration optionTypeIntegerRegistration(Supplier<CommandRegistration.Builder> builder) {
|
||||
return builder.get()
|
||||
.command(REG, "option-type-integer")
|
||||
.group(GROUP)
|
||||
.withOption()
|
||||
@@ -212,8 +213,8 @@ public class OptionTypeCommands extends BaseE2ECommands {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CommandRegistration optionTypeEnumRegistration() {
|
||||
return CommandRegistration.builder()
|
||||
public CommandRegistration optionTypeEnumRegistration(Supplier<CommandRegistration.Builder> builder) {
|
||||
return builder.get()
|
||||
.command(REG, "option-type-enum")
|
||||
.group(GROUP)
|
||||
.withOption()
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package org.springframework.shell.samples.e2e;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.shell.command.CommandRegistration;
|
||||
import org.springframework.shell.standard.ShellComponent;
|
||||
@@ -37,8 +39,8 @@ public class OptionalValueCommands extends BaseE2ECommands {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CommandRegistration testOptionalValueRegistration() {
|
||||
return CommandRegistration.builder()
|
||||
public CommandRegistration testOptionalValueRegistration(Supplier<CommandRegistration.Builder> builder) {
|
||||
return builder.get()
|
||||
.command(REG, "optional-value")
|
||||
.group(GROUP)
|
||||
.withOption()
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package org.springframework.shell.samples.e2e;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.shell.command.CommandRegistration;
|
||||
import org.springframework.shell.standard.ShellComponent;
|
||||
@@ -37,8 +39,8 @@ public class RequiredValueCommands extends BaseE2ECommands {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CommandRegistration testRequiredValueRegistration() {
|
||||
return CommandRegistration.builder()
|
||||
public CommandRegistration testRequiredValueRegistration(Supplier<CommandRegistration.Builder> builder) {
|
||||
return builder.get()
|
||||
.command(REG, "required-value")
|
||||
.group(GROUP)
|
||||
.withOption()
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package org.springframework.shell.samples.e2e;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import jakarta.validation.constraints.Min;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -40,8 +42,8 @@ public class ValidatedValueCommands extends BaseE2ECommands {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CommandRegistration testValidatedValueRegistration() {
|
||||
return CommandRegistration.builder()
|
||||
public CommandRegistration testValidatedValueRegistration(Supplier<CommandRegistration.Builder> builder) {
|
||||
return builder.get()
|
||||
.command(REG, "validated-value")
|
||||
.group(GROUP)
|
||||
.withOption()
|
||||
|
||||
@@ -30,7 +30,6 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.core.DefaultParameterNameDiscoverer;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
@@ -62,14 +61,16 @@ import org.springframework.util.StringUtils;
|
||||
* @author Camilo Gonzalez
|
||||
* @author Janne Valkealahti
|
||||
*/
|
||||
public class StandardMethodTargetRegistrar implements MethodTargetRegistrar, ApplicationContextAware {
|
||||
public class StandardMethodTargetRegistrar implements MethodTargetRegistrar {
|
||||
|
||||
private final Logger log = LoggerFactory.getLogger(StandardMethodTargetRegistrar.class);
|
||||
private ApplicationContext applicationContext;
|
||||
private Supplier<CommandRegistration.Builder> commandRegistrationBuilderSupplier;
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) {
|
||||
public StandardMethodTargetRegistrar(ApplicationContext applicationContext,
|
||||
Supplier<CommandRegistration.Builder> commandRegistrationBuilderSupplier) {
|
||||
this.applicationContext = applicationContext;
|
||||
this.commandRegistrationBuilderSupplier = commandRegistrationBuilderSupplier;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -90,7 +91,7 @@ public class StandardMethodTargetRegistrar implements MethodTargetRegistrar, App
|
||||
log.debug("Registering with keys='{}' key='{}'", keys, key);
|
||||
Supplier<Availability> availabilityIndicator = findAvailabilityIndicator(keys, bean, method);
|
||||
|
||||
Builder builder = CommandRegistration.builder()
|
||||
Builder builder = commandRegistrationBuilderSupplier.get()
|
||||
.command(key)
|
||||
.group(group)
|
||||
.description(shellMapping.value())
|
||||
@@ -210,7 +211,6 @@ public class StandardMethodTargetRegistrar implements MethodTargetRegistrar, App
|
||||
}
|
||||
|
||||
builder.withTarget().method(bean, method);
|
||||
|
||||
CommandRegistration registration = builder.build();
|
||||
registry.register(registration);
|
||||
}, method -> method.getAnnotation(ShellMethod.class) != null);
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.springframework.shell.standard;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
@@ -42,10 +43,11 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
*/
|
||||
public class StandardMethodTargetRegistrarTests {
|
||||
|
||||
private StandardMethodTargetRegistrar registrar = new StandardMethodTargetRegistrar();
|
||||
private StandardMethodTargetRegistrar registrar;
|
||||
private AnnotationConfigApplicationContext applicationContext;
|
||||
private CommandCatalog catalog;
|
||||
private DefaultShellContext shellContext;
|
||||
private Supplier<CommandRegistration.Builder> builder = () -> CommandRegistration.builder();
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
@@ -65,7 +67,7 @@ public class StandardMethodTargetRegistrarTests {
|
||||
@Test
|
||||
public void testRegistrations() {
|
||||
applicationContext = new AnnotationConfigApplicationContext(Sample1.class);
|
||||
registrar.setApplicationContext(applicationContext);
|
||||
registrar = new StandardMethodTargetRegistrar(applicationContext, builder);
|
||||
registrar.register(catalog);
|
||||
Map<String, CommandRegistration> registrations = catalog.getRegistrations();
|
||||
assertThat(registrations).hasSize(3);
|
||||
@@ -100,7 +102,7 @@ public class StandardMethodTargetRegistrarTests {
|
||||
@Test
|
||||
public void testOptionRequiredWithAnnotation() {
|
||||
applicationContext = new AnnotationConfigApplicationContext(Sample2.class);
|
||||
registrar.setApplicationContext(applicationContext);
|
||||
registrar = new StandardMethodTargetRegistrar(applicationContext, builder);
|
||||
registrar.register(catalog);
|
||||
Map<String, CommandRegistration> registrations = catalog.getRegistrations();
|
||||
assertThat(registrations).hasSize(1);
|
||||
@@ -122,7 +124,7 @@ public class StandardMethodTargetRegistrarTests {
|
||||
@Test
|
||||
public void testOptionOptionalWithAnnotation() {
|
||||
applicationContext = new AnnotationConfigApplicationContext(Sample3.class);
|
||||
registrar.setApplicationContext(applicationContext);
|
||||
registrar = new StandardMethodTargetRegistrar(applicationContext, builder);
|
||||
registrar.register(catalog);
|
||||
Map<String, CommandRegistration> registrations = catalog.getRegistrations();
|
||||
assertThat(registrations).hasSize(1);
|
||||
@@ -146,7 +148,7 @@ public class StandardMethodTargetRegistrarTests {
|
||||
public void testAvailabilityIndicators() {
|
||||
applicationContext = new AnnotationConfigApplicationContext(SampleWithAvailability.class);
|
||||
SampleWithAvailability sample = applicationContext.getBean(SampleWithAvailability.class);
|
||||
registrar.setApplicationContext(applicationContext);
|
||||
registrar = new StandardMethodTargetRegistrar(applicationContext, builder);
|
||||
registrar.register(catalog);
|
||||
Map<String, CommandRegistration> registrations = catalog.getRegistrations();
|
||||
|
||||
@@ -219,7 +221,7 @@ public class StandardMethodTargetRegistrarTests {
|
||||
@Test
|
||||
public void testAvailabilityIndicatorErrorMultipleExplicit() {
|
||||
applicationContext = new AnnotationConfigApplicationContext(WrongAvailabilityIndicatorOnShellMethod.class);
|
||||
registrar.setApplicationContext(applicationContext);
|
||||
registrar = new StandardMethodTargetRegistrar(applicationContext, builder);
|
||||
|
||||
assertThatThrownBy(() -> {
|
||||
registrar.register(catalog);
|
||||
@@ -241,7 +243,7 @@ public class StandardMethodTargetRegistrarTests {
|
||||
@Test
|
||||
public void testAvailabilityIndicatorWildcardNotAlone() {
|
||||
applicationContext = new AnnotationConfigApplicationContext(WrongAvailabilityIndicatorWildcardNotAlone.class);
|
||||
registrar.setApplicationContext(applicationContext);
|
||||
registrar = new StandardMethodTargetRegistrar(applicationContext, builder);
|
||||
|
||||
assertThatThrownBy(() -> {
|
||||
registrar.register(catalog);
|
||||
@@ -267,7 +269,7 @@ public class StandardMethodTargetRegistrarTests {
|
||||
@Test
|
||||
public void testAvailabilityIndicatorAmbiguous() {
|
||||
applicationContext = new AnnotationConfigApplicationContext(WrongAvailabilityIndicatorAmbiguous.class);
|
||||
registrar.setApplicationContext(applicationContext);
|
||||
registrar = new StandardMethodTargetRegistrar(applicationContext, builder);
|
||||
|
||||
assertThatThrownBy(() -> {
|
||||
registrar.register(catalog);
|
||||
@@ -301,7 +303,7 @@ public class StandardMethodTargetRegistrarTests {
|
||||
public void testGrouping() {
|
||||
applicationContext = new AnnotationConfigApplicationContext(GroupOneCommands.class,
|
||||
GroupTwoCommands.class, GroupThreeCommands.class);
|
||||
registrar.setApplicationContext(applicationContext);
|
||||
registrar = new StandardMethodTargetRegistrar(applicationContext, builder);
|
||||
registrar.register(catalog);
|
||||
|
||||
assertThat(catalog.getRegistrations().get("explicit1")).satisfies(registration -> {
|
||||
@@ -334,7 +336,7 @@ public class StandardMethodTargetRegistrarTests {
|
||||
public void testInteractionModeInteractive() {
|
||||
shellContext.setInteractionMode(InteractionMode.INTERACTIVE);
|
||||
applicationContext = new AnnotationConfigApplicationContext(InteractionModeCommands.class);
|
||||
registrar.setApplicationContext(applicationContext);
|
||||
registrar = new StandardMethodTargetRegistrar(applicationContext, builder);
|
||||
registrar.register(catalog);
|
||||
|
||||
assertThat(catalog.getRegistrations().get("foo1")).isNotNull();
|
||||
@@ -346,7 +348,7 @@ public class StandardMethodTargetRegistrarTests {
|
||||
public void testInteractionModeNonInteractive() {
|
||||
shellContext.setInteractionMode(InteractionMode.NONINTERACTIVE);
|
||||
applicationContext = new AnnotationConfigApplicationContext(InteractionModeCommands.class);
|
||||
registrar.setApplicationContext(applicationContext);
|
||||
registrar = new StandardMethodTargetRegistrar(applicationContext, builder);
|
||||
registrar.register(catalog);
|
||||
|
||||
assertThat(catalog.getRegistrations().get("foo1")).isNull();
|
||||
@@ -374,7 +376,7 @@ public class StandardMethodTargetRegistrarTests {
|
||||
public void testOptionUseDefaultValue() {
|
||||
shellContext.setInteractionMode(InteractionMode.NONINTERACTIVE);
|
||||
applicationContext = new AnnotationConfigApplicationContext(DefaultValuesCommands.class);
|
||||
registrar.setApplicationContext(applicationContext);
|
||||
registrar = new StandardMethodTargetRegistrar(applicationContext, builder);
|
||||
registrar.register(catalog);
|
||||
|
||||
assertThat(catalog.getRegistrations().get("foo1")).isNotNull();
|
||||
@@ -409,7 +411,7 @@ public class StandardMethodTargetRegistrarTests {
|
||||
@Test
|
||||
public void testOptionValuesWithBoolean() {
|
||||
applicationContext = new AnnotationConfigApplicationContext(ValuesWithBoolean.class);
|
||||
registrar.setApplicationContext(applicationContext);
|
||||
registrar = new StandardMethodTargetRegistrar(applicationContext, builder);
|
||||
registrar.register(catalog);
|
||||
|
||||
assertThat(catalog.getRegistrations().get("foo1")).isNotNull();
|
||||
@@ -447,7 +449,7 @@ public class StandardMethodTargetRegistrarTests {
|
||||
@Test
|
||||
public void testOptionWithoutHyphenRegisterFromDefaultPrefix() {
|
||||
applicationContext = new AnnotationConfigApplicationContext(OptionWithoutHyphenRegisterFromDefaultPrefix.class);
|
||||
registrar.setApplicationContext(applicationContext);
|
||||
registrar = new StandardMethodTargetRegistrar(applicationContext, builder);
|
||||
registrar.register(catalog);
|
||||
|
||||
assertThat(catalog.getRegistrations().get("foo1")).isNotNull();
|
||||
@@ -467,7 +469,7 @@ public class StandardMethodTargetRegistrarTests {
|
||||
@Test
|
||||
public void testOptionWithoutHyphenRegisterFromChangedPrefix() {
|
||||
applicationContext = new AnnotationConfigApplicationContext(OptionWithoutHyphenRegisterFromChangedPrefix.class);
|
||||
registrar.setApplicationContext(applicationContext);
|
||||
registrar = new StandardMethodTargetRegistrar(applicationContext, builder);
|
||||
registrar.register(catalog);
|
||||
|
||||
assertThat(catalog.getRegistrations().get("foo1")).isNotNull();
|
||||
|
||||
Reference in New Issue
Block a user