Fix alias usage with Command annotation

- Fix alias command extraction from existing @Command
  annotations so that we actually get multiple aliases
  defined if more than one defined on a method level.
- Fix rendering issue in a help stg template when
  multiple aliases exists.
- Fixes #796
This commit is contained in:
Janne Valkealahti
2023-06-22 16:00:00 +01:00
parent 811f1f9a55
commit cdd703efa4
6 changed files with 87 additions and 13 deletions

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.shell.command.annotation.support;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.springframework.core.annotation.MergedAnnotation;
@@ -85,10 +87,10 @@ class CommandAnnotationUtils {
*
* @param left the left side annotation
* @param right the right side annotation
* @return deduced boolean for alias field
* @return deduced arrays for alias field
*/
static String[] deduceAlias(MergedAnnotation<?> left, MergedAnnotation<?> right) {
return deduceStringArray(ALIAS, left, right);
static String[][] deduceAlias(MergedAnnotation<?> left, MergedAnnotation<?> right) {
return deduceStringArrayLeftPrefixes(ALIAS, left, right);
}
/**
@@ -149,6 +151,21 @@ class CommandAnnotationUtils {
return mode;
}
private static String[][] deduceStringArrayLeftPrefixes(String field, MergedAnnotation<?> left, MergedAnnotation<?> right) {
List<String> prefix = Stream.of(left.getStringArray(field))
.flatMap(command -> Stream.of(command.split(" ")))
.filter(command -> StringUtils.hasText(command))
.map(command -> command.strip())
.collect(Collectors.toList());
return Stream.of(right.getStringArray(field))
.flatMap(command -> Stream.of(command.split(" ")))
.filter(command -> StringUtils.hasText(command))
.map(command -> command.strip())
.map(command -> Stream.concat(prefix.stream(), Stream.of(command)).collect(Collectors.toList()))
.map(arr -> arr.toArray(String[]::new))
.toArray(String[][]::new);
}
private static String[] deduceStringArray(String field, MergedAnnotation<?> left, MergedAnnotation<?> right) {
return Stream.of(left.getStringArray(field), right.getStringArray(field))

View File

@@ -189,9 +189,9 @@ class CommandRegistrationFactoryBean implements FactoryBean<CommandRegistration>
}
// alias
String[] deduceAlias = CommandAnnotationUtils.deduceAlias(classAnn, methodAnn);
if (deduceAlias.length > 0) {
builder.withAlias().command(deduceAlias);
String[][] deduceAlias = CommandAnnotationUtils.deduceAlias(classAnn, methodAnn);
for (String[] a : deduceAlias) {
builder.withAlias().command(a);
}
// target

View File

@@ -133,13 +133,13 @@ class CommandAnnotationUtilsTests {
void testAlias() {
assertThat(CommandAnnotationUtils.deduceAlias(aliasDefault, aliasDefault)).isEmpty();
assertThat(CommandAnnotationUtils.deduceAlias(aliasDefault, aliasValues1))
.isEqualTo(new String[] { "one", "two" });
.isEqualTo(new String[][] { { "one" }, { "two" } });
assertThat(CommandAnnotationUtils.deduceAlias(aliasValues1, aliasValues2))
.isEqualTo(new String[] { "one", "two", "three", "four" });
.isEqualTo(new String[][] { { "one", "two", "three" }, { "one", "two", "four" } });
assertThat(CommandAnnotationUtils.deduceAlias(aliasDefault, aliasValues3))
.isEqualTo(new String[] { "five", "six", "seven" });
.isEqualTo(new String[][] { { "five" }, { "six" }, { "seven" } });
assertThat(CommandAnnotationUtils.deduceAlias(aliasDefault, aliasValues4))
.isEqualTo(new String[] { "eight", "nine" });
.isEqualTo(new String[][] { { "eight" }, { "nine" } });
}
private static MergedAnnotation<Command> groupValue1 = MergedAnnotations.from(GroupValues1.class)

View File

@@ -63,7 +63,7 @@ class CommandRegistrationFactoryBeanTests {
@Test
void commandCommonThings() {
configCommon(OnBothClassAndMethod.class, new OnBothClassAndMethod())
configCommon(OnBothClassAndMethod.class, new OnBothClassAndMethod(), "command1", new Class[] { })
.run((context) -> {
CommandRegistrationFactoryBean fb = context.getBean(FACTORYBEANREF,
CommandRegistrationFactoryBean.class);
@@ -75,13 +75,30 @@ class CommandRegistrationFactoryBeanTests {
assertThat(registration.getAliases().get(0).getCommand()).isEqualTo("three four");
assertThat(registration.getGroup()).isEqualTo("group2");
});
configCommon(OnBothClassAndMethod.class, new OnBothClassAndMethod(), "command2", new Class[] { })
.run((context) -> {
CommandRegistrationFactoryBean fb = context.getBean(FACTORYBEANREF,
CommandRegistrationFactoryBean.class);
assertThat(fb).isNotNull();
CommandRegistration registration = fb.getObject();
assertThat(registration).isNotNull();
assertThat(registration.getCommand()).isEqualTo("one three");
assertThat(registration.getAliases()).hasSize(2);
assertThat(registration.getAliases().get(0).getCommand()).isEqualTo("three four");
assertThat(registration.getAliases().get(1).getCommand()).isEqualTo("three five");
assertThat(registration.getGroup()).isEqualTo("group2");
});
}
@Command(command = "one", alias = "three", group = "group1")
private static class OnBothClassAndMethod {
@Command(command = "two", alias = "four", group = "group2")
void command(){
void command1(){
}
@Command(command = "three", alias = { "four", "five" }, group = "group2")
void command2(){
}
}

View File

@@ -18,10 +18,26 @@ package org.springframework.shell.samples.e2e;
import org.springframework.context.annotation.Bean;
import org.springframework.shell.command.CommandRegistration;
import org.springframework.shell.command.annotation.Command;
import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;
import org.springframework.stereotype.Component;
public class AliasCommands {
@ShellComponent
public static class LegacyAnnotation extends BaseE2ECommands {
@ShellMethod(key = { LEGACY_ANNO + "alias-1", LEGACY_ANNO + "aliasfor-1" }, group = GROUP)
public String testAlias1LegacyAnnotation() {
return "Hello from alias command";
}
@ShellMethod(key = { LEGACY_ANNO + "alias-2", LEGACY_ANNO + "alias1for-2", LEGACY_ANNO + "alias2for-2" }, group = GROUP)
public String testAlias2LegacyAnnotation() {
return "Hello from alias command";
}
}
@Command(command = BaseE2ECommands.ANNO, alias = BaseE2ECommands.ANNO, group = BaseE2ECommands.GROUP)
public static class AliasCommandsAnnotation extends BaseE2ECommands {
@@ -29,6 +45,11 @@ public class AliasCommands {
public String testAlias1Annotation() {
return "Hello from alias command";
}
@Command(command = "alias-2", alias = { "alias1for-2", "alias2for-2" })
public String testAlias2Annotation() {
return "Hello from alias command";
}
}
@Component
@@ -49,6 +70,25 @@ public class AliasCommands {
.and()
.build();
}
@Bean
public CommandRegistration testAlias2Registration(CommandRegistration.BuilderSupplier builder) {
return builder.get()
.command(REG, "alias-2")
.group(GROUP)
.withAlias()
.command(REG, "alias1for-2")
.and()
.withAlias()
.command(REG, "alias2for-2")
.and()
.withTarget()
.function(ctx -> {
return "Hello from alias command";
})
.and()
.build();
}
}
}

View File

@@ -70,7 +70,7 @@ availability(availability) ::= <<
aliases(aliases) ::= <<
<if(aliases)>
<("ALSO KNOWN AS"); format="style-highlight">
<(aliases); separator=", ">
<aliases: { a | <a>}; separator=", ">
<endif>
>>