OptionValues supports multiple resolvers

- Change ref field in OptionValues to providers
  takin an array.
- Relates #637
This commit is contained in:
Janne Valkealahti
2023-03-05 16:38:01 +00:00
parent d4506d2583
commit eaa3e5caf9
4 changed files with 62 additions and 11 deletions

View File

@@ -33,8 +33,9 @@ import java.lang.annotation.Target;
public @interface OptionValues {
/**
* Reference to a bean name
* @return a bean name
* Names of beans for {@link CompletionProvider}.
*
* @return names of CompletionProvider beans
*/
String ref() default "";
String[] provider() default {};
}

View File

@@ -17,6 +17,7 @@ package org.springframework.shell.command.annotation.support;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -38,6 +39,8 @@ import org.springframework.core.annotation.MergedAnnotations.SearchStrategy;
import org.springframework.messaging.handler.invocation.InvocableHandlerMethod;
import org.springframework.shell.Availability;
import org.springframework.shell.AvailabilityProvider;
import org.springframework.shell.CompletionContext;
import org.springframework.shell.CompletionProposal;
import org.springframework.shell.Utils;
import org.springframework.shell.command.CommandExceptionResolver;
import org.springframework.shell.command.CommandHandlingResult;
@@ -52,6 +55,7 @@ import org.springframework.shell.command.annotation.Option;
import org.springframework.shell.command.annotation.OptionValues;
import org.springframework.shell.command.invocation.InvocableShellMethod;
import org.springframework.shell.completion.CompletionProvider;
import org.springframework.shell.completion.CompletionResolver;
import org.springframework.shell.context.InteractionMode;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
@@ -277,12 +281,25 @@ class CommandRegistrationFactoryBean implements FactoryBean<CommandRegistration>
}
OptionValues ovAnn = mp.getParameterAnnotation(OptionValues.class);
if (ovAnn != null && StringUtils.hasText(ovAnn.ref())) {
CompletionProvider cr = this.applicationContext.getBean(ovAnn.ref(), CompletionProvider.class);
if (cr != null) {
optionSpec.completion(ctx -> cr.apply(ctx));
if (ovAnn != null) {
String[] providerBeanNames = ovAnn.provider();
if (providerBeanNames.length > 0) {
final List<CompletionProvider> resolvers = Arrays.stream(providerBeanNames)
.map(beanName -> this.applicationContext.getBean(beanName, CompletionProvider.class))
.collect(Collectors.toList());
optionSpec.completion(ctx -> {
return resolvers.stream()
.flatMap(resolver -> resolver.apply(ctx).stream())
.collect(Collectors.toList());
});
}
}
// if (ovAnn != null && StringUtils.hasText(ovAnn.ref())) {
// CompletionProvider cr = this.applicationContext.getBean(ovAnn.ref(), CompletionProvider.class);
// if (cr != null) {
// optionSpec.completion(ctx -> cr.apply(ctx));
// }
// }
}
}

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.shell.command.annotation.support;
import java.util.Collections;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
@@ -25,6 +27,8 @@ import org.springframework.shell.command.CommandRegistration;
import org.springframework.shell.command.annotation.Command;
import org.springframework.shell.command.annotation.CommandAvailability;
import org.springframework.shell.command.annotation.Option;
import org.springframework.shell.command.annotation.OptionValues;
import org.springframework.shell.completion.CompletionProvider;
import static org.assertj.core.api.Assertions.assertThat;
@@ -209,6 +213,35 @@ class CommandRegistrationFactoryBeanTests {
}
}
@Test
void setsOptionWithCompletion() {
configCommon(OptionWithCompletion.class, new OptionWithCompletion(), "command1", new Class[] { String.class })
.run((context) -> {
CommandRegistrationFactoryBean fb = context.getBean(FACTORYBEANREF,
CommandRegistrationFactoryBean.class);
assertThat(fb).isNotNull();
CommandRegistration registration = fb.getObject();
assertThat(registration).isNotNull();
assertThat(registration.getOptions().get(0).getCompletion()).isNotNull();
});
}
@Command
private static class OptionWithCompletion {
@Command
void command1(@Option(longNames = "arg") @OptionValues(provider = "completionProvider") String arg) {
}
@Bean
CompletionProvider completionProvider() {
return ctx -> {
return Collections.emptyList();
};
}
}
private <T> ApplicationContextRunner configCommon(Class<T> type, T bean) {
return configCommon(type, bean, "command", new Class[0]);
}

View File

@@ -62,8 +62,8 @@ public class InteractiveCompletionCommands {
@Command(command = "interactive-completion-1")
public String testRequiredValueAnnotation(
@Option(longNames = "arg1", required = true) @OptionValues(ref = "test1CompletionProvider") String arg1,
@Option(longNames = "arg2", required = true) @OptionValues(ref = "test2CompletionProvider") String arg2
@Option(longNames = "arg1", required = true) @OptionValues(provider = "test1CompletionProvider") String arg1,
@Option(longNames = "arg2", required = true) @OptionValues(provider = "test2CompletionProvider") String arg2
) {
return "Hello " + arg1;
}
@@ -79,8 +79,8 @@ public class InteractiveCompletionCommands {
@Bean
CompletionProvider test2CompletionProvider() {
return ctx -> {
Test1ValuesProvider test1ValuesProvider = new Test1ValuesProvider();
return test1ValuesProvider.complete(ctx);
Test2ValuesProvider test2ValuesProvider = new Test2ValuesProvider();
return test2ValuesProvider.complete(ctx);
};
}
}