Separate interactive and non-interactive commands

- Add new ShellContext concept which now is just a way
  to stash info about interaction mode where ShellRunner
  can update supported mode.
- ShellMethod has a new field interactionMode which user
  can use to define commands between interactive/non-interactive
  modes which then prevents CommandRegistry to show
  commands at runtime.
- Fixes #345
This commit is contained in:
Janne Valkealahti
2022-01-09 14:10:15 +00:00
parent 3cb3309d68
commit dbe8a8b408
21 changed files with 284 additions and 32 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2015 the original author or authors.
* Copyright 2015-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.
@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.shell.standard;
import java.lang.annotation.Documented;
@@ -22,6 +21,8 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.shell.context.InteractionMode;
/**
* Used to mark a method as invokable via Spring Shell.
*
@@ -68,4 +69,14 @@ public @interface ShellMethod {
*/
String group() default INHERITED;
/**
* Defines interaction mode for a command as a hint when command should be
* available. For example presense of some commands doesn't make sense if shell
* is running as non-interactive mode and vice versa.
*
* Defaults to {@link InteractionMode#ALL}
*
* @return interaction mode
*/
InteractionMode interactionMode() default InteractionMode.ALL;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2015-2017 the original author or authors.
* Copyright 2015-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.
@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.shell.standard;
import java.lang.reflect.Method;
@@ -76,7 +75,8 @@ public class StandardMethodTargetRegistrar implements MethodTargetRegistrar, App
String group = getOrInferGroup(method);
for (String key : keys) {
Supplier<Availability> availabilityIndicator = findAvailabilityIndicator(keys, bean, method);
MethodTarget target = new MethodTarget(method, bean, new Command.Help(shellMapping.value(), group), availabilityIndicator);
MethodTarget target = new MethodTarget(method, bean, new Command.Help(shellMapping.value(), group),
availabilityIndicator, shellMapping.interactionMode());
registry.register(key, target);
commands.put(key, target);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2017 the original author or authors.
* Copyright 2017-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.
@@ -26,6 +26,8 @@ import org.springframework.context.annotation.AnnotationConfigApplicationContext
import org.springframework.shell.Availability;
import org.springframework.shell.ConfigurableCommandRegistry;
import org.springframework.shell.MethodTarget;
import org.springframework.shell.context.DefaultShellContext;
import org.springframework.shell.context.InteractionMode;
import org.springframework.shell.standard.test1.GroupOneCommands;
import org.springframework.shell.standard.test2.GroupThreeCommands;
import org.springframework.shell.standard.test2.GroupTwoCommands;
@@ -42,7 +44,7 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy;
public class StandardMethodTargetRegistrarTest {
private StandardMethodTargetRegistrar registrar = new StandardMethodTargetRegistrar();
private ConfigurableCommandRegistry registry = new ConfigurableCommandRegistry();
private ConfigurableCommandRegistry registry = new ConfigurableCommandRegistry(new DefaultShellContext());
@Test
public void testRegistrations() {
@@ -256,4 +258,47 @@ public class StandardMethodTargetRegistrarTest {
Assertions.assertThat(commands.get("implicit3").getGroup()).isEqualTo("Explicit Group 3 Class Level");
}
@Test
public void testInteractionModeInteractive() {
DefaultShellContext shellContext = new DefaultShellContext();
shellContext.setInteractionMode(InteractionMode.INTERACTIVE);
registry = new ConfigurableCommandRegistry(shellContext);
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(InteractionModeCommands.class);
registrar.setApplicationContext(applicationContext);
registrar.register(registry);
assertThat(registry.listCommands().get("foo1")).isNotNull();
assertThat(registry.listCommands().get("foo2")).isNull();
assertThat(registry.listCommands().get("foo3")).isNotNull();
}
@Test
public void testInteractionModeNonInteractive() {
DefaultShellContext shellContext = new DefaultShellContext();
shellContext.setInteractionMode(InteractionMode.NONINTERACTIVE);
registry = new ConfigurableCommandRegistry(shellContext);
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(InteractionModeCommands.class);
registrar.setApplicationContext(applicationContext);
registrar.register(registry);
assertThat(registry.listCommands().get("foo1")).isNull();
assertThat(registry.listCommands().get("foo2")).isNotNull();
assertThat(registry.listCommands().get("foo3")).isNotNull();
}
@ShellComponent
public static class InteractionModeCommands {
@ShellMethod(value = "foo1", interactionMode = InteractionMode.INTERACTIVE)
public void foo1() {
}
@ShellMethod(value = "foo2", interactionMode = InteractionMode.NONINTERACTIVE)
public void foo2() {
}
@ShellMethod(value = "foo3")
public void foo3() {
}
}
}

View File

@@ -29,6 +29,7 @@ import org.springframework.shell.CommandRegistry;
import org.springframework.shell.ConfigurableCommandRegistry;
import org.springframework.shell.MethodTarget;
import org.springframework.shell.ParameterResolver;
import org.springframework.shell.context.DefaultShellContext;
import org.springframework.shell.standard.ShellMethod;
import org.springframework.shell.standard.ShellOption;
import org.springframework.shell.standard.StandardParameterResolver;
@@ -42,7 +43,7 @@ public class AbstractCompletionsTests {
@Test
public void testBasicModelGeneration() {
DefaultResourceLoader resourceLoader = new DefaultResourceLoader();
ConfigurableCommandRegistry commandRegistry = new ConfigurableCommandRegistry();
ConfigurableCommandRegistry commandRegistry = new ConfigurableCommandRegistry(new DefaultShellContext());
List<ParameterResolver> parameterResolvers = new ArrayList<>();
StandardParameterResolver resolver = new StandardParameterResolver(new DefaultConversionService(),
Collections.emptySet());
@@ -91,7 +92,7 @@ public class AbstractCompletionsTests {
@Test
public void testBuilder() {
DefaultResourceLoader resourceLoader = new DefaultResourceLoader();
ConfigurableCommandRegistry commandRegistry = new ConfigurableCommandRegistry();
ConfigurableCommandRegistry commandRegistry = new ConfigurableCommandRegistry(new DefaultShellContext());
List<ParameterResolver> parameterResolvers = new ArrayList<>();
TestCompletions completions = new TestCompletions(resourceLoader, commandRegistry, parameterResolvers);

View File

@@ -25,6 +25,7 @@ import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.shell.ConfigurableCommandRegistry;
import org.springframework.shell.ParameterResolver;
import org.springframework.shell.context.DefaultShellContext;
import static org.assertj.core.api.Assertions.assertThat;
@@ -48,7 +49,7 @@ public class BashCompletionsTests {
@Test
public void testDoesNotError() {
ConfigurableCommandRegistry commandRegistry = new ConfigurableCommandRegistry();
ConfigurableCommandRegistry commandRegistry = new ConfigurableCommandRegistry(new DefaultShellContext());
List<ParameterResolver> parameterResolvers = new ArrayList<>();
BashCompletions completions = new BashCompletions(context, commandRegistry, parameterResolvers);
String bash = completions.generate("root-command");