Rework help command

- Change help command output to get templated using
  model classes.
- Remove things around ParameterDescription as those are
  replaced with template classes.
- Fixes for native configs.
- For now availability and aliases are removed from
  help to get back in better form.
- Aliases has been partly introduced to structure.
- Fixes #422
This commit is contained in:
Janne Valkealahti
2022-05-26 07:45:35 +01:00
committed by GitHub
parent eed1d84653
commit bd9ab62013
33 changed files with 1055 additions and 794 deletions

View File

@@ -19,9 +19,8 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Collection;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import javax.validation.constraints.Max;
@@ -32,10 +31,8 @@ import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInfo;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
@@ -44,6 +41,11 @@ import org.springframework.shell.command.CommandRegistration;
import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;
import org.springframework.shell.standard.ShellOption;
import org.springframework.shell.style.TemplateExecutor;
import org.springframework.shell.style.Theme;
import org.springframework.shell.style.ThemeRegistry;
import org.springframework.shell.style.ThemeResolver;
import org.springframework.shell.style.ThemeSettings;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.util.FileCopyUtils;
@@ -57,10 +59,9 @@ public class HelpTests {
private static Locale previousLocale;
private String testName;
private Map<String, CommandRegistration> registrations = new HashMap<>();
private CommandsPojo commandsPojo = new CommandsPojo();
@MockBean
@Autowired
private CommandCatalog commandCatalog;
@Autowired
@@ -79,12 +80,14 @@ public class HelpTests {
@BeforeEach
public void setup(TestInfo testInfo) {
registrations.clear();
Optional<Method> testMethod = testInfo.getTestMethod();
if (testMethod.isPresent()) {
this.testName = testMethod.get().getName();
}
Mockito.when(commandCatalog.getRegistrations()).thenReturn(registrations);
Collection<CommandRegistration> regs = this.commandCatalog.getRegistrations().values();
regs.stream().forEach(r -> {
this.commandCatalog.unregister(r);
});
}
@Test
@@ -117,66 +120,22 @@ public class HelpTests {
.type(float[].class)
.and()
.build();
registrations.put("first-command", registration);
registrations.put("1st-command", registration);
commandCatalog.register(registration);
CharSequence help = this.help.help("first-command").toString();
assertThat(help).isEqualTo(sample());
}
@Test
public void testCommandList() throws Exception {
CommandRegistration registration1 = CommandRegistration.builder()
.command("first-command")
.description("A rather extensive description of some command.")
.withTarget()
.method(commandsPojo, "firstCommand")
.and()
.withOption()
.shortNames('r')
.and()
.build();
registrations.put("first-command", registration1);
registrations.put("1st-command", registration1);
CommandRegistration registration2 = CommandRegistration.builder()
.command("second-command")
.description("The second command. This one is known under several aliases as well.")
.withTarget()
.method(commandsPojo, "secondCommand")
.and()
.build();
registrations.put("second-command", registration2);
registrations.put("yet-another-command", registration2);
CommandRegistration registration3 = CommandRegistration.builder()
.command("second-command")
.description("The last command.")
.withTarget()
.method(commandsPojo, "thirdCommand")
.and()
.build();
registrations.put("third-command", registration3);
CommandRegistration registration4 = CommandRegistration.builder()
.command("first-group-command")
.description("The first command in a separate group.")
.group("Example Group")
.withTarget()
.method(commandsPojo, "firstCommandInGroup")
.and()
.build();
registrations.put("first-group-command", registration4);
CommandRegistration registration5 = CommandRegistration.builder()
.command("second-group-command")
.description("The second command in a separate group.")
.group("Example Group")
.withTarget()
.method(commandsPojo, "secondCommandInGroup")
.and()
.build();
registrations.put("second-group-command", registration5);
public void testCommandListDefault() throws Exception {
registerCommandListCommands();
String list = this.help.help(null).toString();
assertThat(list).isEqualTo(sample());
}
@Test
public void testCommandListFlat() throws Exception {
registerCommandListCommands();
this.help.setShowGroups(false);
String list = this.help.help(null).toString();
assertThat(list).isEqualTo(sample());
}
@@ -193,18 +152,83 @@ public class HelpTests {
return FileCopyUtils.copyToString(new InputStreamReader(is, "UTF-8")).replace("&", "");
}
private void registerCommandListCommands() throws Exception {
CommandRegistration registration1 = CommandRegistration.builder()
.command("first-command")
.description("A rather extensive description of some command.")
.withAlias()
.command("1st-command")
.and()
.withTarget()
.method(commandsPojo, "firstCommand")
.and()
.withOption()
.shortNames('r')
.and()
.build();
commandCatalog.register(registration1);
CommandRegistration registration2 = CommandRegistration.builder()
.command("second-command")
.description("The second command. This one is known under several aliases as well.")
.withAlias()
.command("yet-another-command")
.and()
.withTarget()
.method(commandsPojo, "secondCommand")
.and()
.build();
commandCatalog.register(registration2);
CommandRegistration registration3 = CommandRegistration.builder()
.command("third-command")
.description("The last command.")
.withTarget()
.method(commandsPojo, "thirdCommand")
.and()
.build();
commandCatalog.register(registration3);
CommandRegistration registration4 = CommandRegistration.builder()
.command("first-group-command")
.description("The first command in a separate group.")
.group("Example Group")
.withTarget()
.method(commandsPojo, "firstCommandInGroup")
.and()
.build();
commandCatalog.register(registration4);
CommandRegistration registration5 = CommandRegistration.builder()
.command("second-group-command")
.description("The second command in a separate group.")
.group("Example Group")
.withTarget()
.method(commandsPojo, "secondCommandInGroup")
.and()
.build();
commandCatalog.register(registration5);
}
@Configuration
static class Config {
@Bean
public Help help() {
return new Help();
public CommandCatalog commandCatalog() {
return CommandCatalog.of();
}
// @Bean
// public ParameterResolver parameterResolver() {
// return new StandardParameterResolver(new DefaultConversionService(), Collections.emptySet());
// }
@Bean
public Help help() {
ThemeRegistry registry = new ThemeRegistry();
registry.register(Theme.of("default", ThemeSettings.themeSettings()));
ThemeResolver resolver = new ThemeResolver(registry, "default");
TemplateExecutor executor = new TemplateExecutor(resolver);
Help help = new Help(executor);
help.setCommandTemplate("classpath:template/help-command-default.stg");
help.setCommandsTemplate("classpath:template/help-commands-default.stg");
return help;
}
}
@ShellComponent

View File

@@ -1,28 +1,23 @@
NAME
first-command - A rather extensive description of some command.
first-command - A rather extensive description of some command.
SYNOPSYS
first-command [[-r] boolean] [[-f] boolean] [[-n] int] [-o] float[]
SYNOPSIS
first-command -r boolean -f boolean -n int -o float[]
OPTIONS
-r boolean
Whether to delete recursively
[Optional, default = false]
-r boolean
Whether to delete recursively
[Optional]
-f boolean
Do not ask for confirmation. YOLO
[Optional, default = false]
-f boolean
Do not ask for confirmation. YOLO
[Optional]
-n int
The answer to everything
[Optional, default = 42]
-n int
The answer to everything
[Optional, default = 42]
-o float[]
Some other parameters
[Mandatory]
ALSO KNOWN AS
1st-command
-o float[]
Some other parameters
[Optional]

View File

@@ -1,11 +0,0 @@
AVAILABLE COMMANDS&
&
Default&
1st-command, first-command: A rather extensive description of some command.&
second-command, yet-another-command: The second command. This one is known under several aliases as well.&
third-command: The last command.&
&
Example Group&
first-group-command: The first command in a separate group.&
second-group-command: The second command in a separate group.&
&

View File

@@ -0,0 +1,14 @@
AVAILABLE COMMANDS
Default
1st-command: A rather extensive description of some command.
yet-another-command: The second command. This one is known under several aliases as well.
third-command: The last command.
second-command: The second command. This one is known under several aliases as well.
first-command: A rather extensive description of some command.
Example Group
second-group-command: The second command in a separate group.
first-group-command: The first command in a separate group.

View File

@@ -0,0 +1,10 @@
AVAILABLE COMMANDS
1st-command: A rather extensive description of some command.
yet-another-command: The second command. This one is known under several aliases as well.
third-command: The last command.
second-command: The second command. This one is known under several aliases as well.
first-command: A rather extensive description of some command.
second-group-command: The second command in a separate group.
first-group-command: The first command in a separate group.