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:
@@ -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.
|
||||
@@ -13,12 +13,14 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.shell;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.shell.context.InteractionMode;
|
||||
import org.springframework.shell.context.ShellContext;
|
||||
|
||||
/**
|
||||
* A {@link CommandRegistry} that supports registration of new commands.
|
||||
@@ -29,11 +31,31 @@ import java.util.TreeMap;
|
||||
*/
|
||||
public class ConfigurableCommandRegistry implements CommandRegistry {
|
||||
|
||||
private final ShellContext shellContext;
|
||||
private Map<String, MethodTarget> commands = new HashMap<>();
|
||||
|
||||
public ConfigurableCommandRegistry(ShellContext shellContext) {
|
||||
this.shellContext = shellContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, MethodTarget> listCommands() {
|
||||
return new TreeMap<>(commands);
|
||||
return commands.entrySet().stream()
|
||||
.filter(e -> {
|
||||
InteractionMode mim = e.getValue().getInteractionMode();
|
||||
InteractionMode cim = shellContext.getInteractionMode();
|
||||
if (mim == null || cim == null || mim == InteractionMode.ALL) {
|
||||
return true;
|
||||
}
|
||||
else if (mim == InteractionMode.INTERACTIVE) {
|
||||
return cim == InteractionMode.INTERACTIVE || cim == InteractionMode.ALL;
|
||||
}
|
||||
else if (mim == InteractionMode.NONINTERACTIVE) {
|
||||
return cim == InteractionMode.NONINTERACTIVE || cim == InteractionMode.ALL;
|
||||
}
|
||||
return true;
|
||||
})
|
||||
.collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue()));
|
||||
}
|
||||
|
||||
public void register(String name, MethodTarget target) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
@@ -21,6 +20,7 @@ import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.shell.context.InteractionMode;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
@@ -37,6 +37,8 @@ public class MethodTarget implements Command {
|
||||
|
||||
private final Help help;
|
||||
|
||||
private final InteractionMode interactionMode;
|
||||
|
||||
/**
|
||||
* If not null, returns whether or not the command is currently available. Implementations must be idempotent.
|
||||
*/
|
||||
@@ -51,6 +53,10 @@ public class MethodTarget implements Command {
|
||||
}
|
||||
|
||||
public MethodTarget(Method method, Object bean, Help help, Supplier<Availability> availabilityIndicator) {
|
||||
this(method, bean, help, availabilityIndicator, null);
|
||||
}
|
||||
|
||||
public MethodTarget(Method method, Object bean, Help help, Supplier<Availability> availabilityIndicator, InteractionMode interactionMode) {
|
||||
Assert.notNull(method, "Method cannot be null");
|
||||
Assert.notNull(bean, "Bean cannot be null");
|
||||
Assert.hasText(help.getDescription(), String.format("Help cannot be blank when trying to define command based on '%s'", method));
|
||||
@@ -59,6 +65,7 @@ public class MethodTarget implements Command {
|
||||
this.bean = bean;
|
||||
this.help = help;
|
||||
this.availabilityIndicator = availabilityIndicator != null ? availabilityIndicator : () -> Availability.available();
|
||||
this.interactionMode = interactionMode;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -103,6 +110,10 @@ public class MethodTarget implements Command {
|
||||
return availabilityIndicator.get();
|
||||
}
|
||||
|
||||
public InteractionMode getInteractionMode() {
|
||||
return interactionMode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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.context;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Default implementation of a {@link ShellContext}.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*/
|
||||
public class DefaultShellContext implements ShellContext {
|
||||
|
||||
private InteractionMode interactionMode = InteractionMode.ALL;
|
||||
|
||||
@Override
|
||||
public InteractionMode getInteractionMode() {
|
||||
return interactionMode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInteractionMode(InteractionMode interactionMode) {
|
||||
Assert.notNull(interactionMode, "mode cannot be null");
|
||||
this.interactionMode = interactionMode;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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.context;
|
||||
|
||||
/**
|
||||
* Enumeration for modes shell is operating.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*/
|
||||
public enum InteractionMode {
|
||||
|
||||
/**
|
||||
* All possible modes.
|
||||
*/
|
||||
ALL,
|
||||
|
||||
/**
|
||||
* Non-interactive mode which is expected to exit and doesn't have any kind of
|
||||
* running mode to keep shell alive.
|
||||
*/
|
||||
NONINTERACTIVE,
|
||||
|
||||
/**
|
||||
* Interactive mode which is expected to not exit and do have a running mode to
|
||||
* keep shell alive.
|
||||
*/
|
||||
INTERACTIVE
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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.context;
|
||||
|
||||
/**
|
||||
* Interface defining a contract for a context which allows to loosely connect
|
||||
* different components together and keep things alive between commands.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*/
|
||||
public interface ShellContext {
|
||||
|
||||
/**
|
||||
* Gets an interaction mode.
|
||||
*
|
||||
* @return a current interaction mode
|
||||
*/
|
||||
InteractionMode getInteractionMode();
|
||||
|
||||
/**
|
||||
* Sets an interaction mode.
|
||||
*
|
||||
* @param interactionMode the interaction mode
|
||||
*/
|
||||
void setInteractionMode(InteractionMode interactionMode);
|
||||
}
|
||||
@@ -27,6 +27,8 @@ import org.springframework.shell.Input;
|
||||
import org.springframework.shell.InputProvider;
|
||||
import org.springframework.shell.Shell;
|
||||
import org.springframework.shell.ShellRunner;
|
||||
import org.springframework.shell.context.InteractionMode;
|
||||
import org.springframework.shell.context.ShellContext;
|
||||
|
||||
/**
|
||||
* Default Boot runner that bootstraps the shell application in interactive
|
||||
@@ -52,14 +54,19 @@ public class InteractiveShellRunner implements ShellRunner {
|
||||
|
||||
private final Shell shell;
|
||||
|
||||
public InteractiveShellRunner(LineReader lineReader, PromptProvider promptProvider, Shell shell) {
|
||||
private final ShellContext shellContext;
|
||||
|
||||
public InteractiveShellRunner(LineReader lineReader, PromptProvider promptProvider, Shell shell,
|
||||
ShellContext shellContext) {
|
||||
this.lineReader = lineReader;
|
||||
this.promptProvider = promptProvider;
|
||||
this.shell = shell;
|
||||
this.shellContext = shellContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(ApplicationArguments args) throws Exception {
|
||||
shellContext.setInteractionMode(InteractionMode.INTERACTIVE);
|
||||
InputProvider inputProvider = new JLineInputProvider(lineReader, promptProvider);
|
||||
shell.run(inputProvider);
|
||||
}
|
||||
|
||||
@@ -24,6 +24,8 @@ import org.springframework.shell.Input;
|
||||
import org.springframework.shell.InputProvider;
|
||||
import org.springframework.shell.Shell;
|
||||
import org.springframework.shell.ShellRunner;
|
||||
import org.springframework.shell.context.InteractionMode;
|
||||
import org.springframework.shell.context.ShellContext;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@@ -37,9 +39,11 @@ import org.springframework.util.StringUtils;
|
||||
public class NonInteractiveShellRunner implements ShellRunner {
|
||||
|
||||
private final Shell shell;
|
||||
private final ShellContext shellContext;
|
||||
|
||||
public NonInteractiveShellRunner(Shell shell) {
|
||||
public NonInteractiveShellRunner(Shell shell, ShellContext shellContext) {
|
||||
this.shell = shell;
|
||||
this.shellContext = shellContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -50,6 +54,7 @@ public class NonInteractiveShellRunner implements ShellRunner {
|
||||
|
||||
@Override
|
||||
public void run(ApplicationArguments args) throws Exception {
|
||||
shellContext.setInteractionMode(InteractionMode.NONINTERACTIVE);
|
||||
List<String> argsToShellCommand = Arrays.asList(args.getSourceArgs());
|
||||
InputProvider inputProvider = new StringInputProvider(argsToShellCommand);
|
||||
shell.run(inputProvider);
|
||||
|
||||
@@ -18,6 +18,8 @@ package org.springframework.shell;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.shell.context.DefaultShellContext;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
|
||||
@@ -30,14 +32,14 @@ public class ConfigurableCommandRegistryTest {
|
||||
|
||||
@Test
|
||||
public void testRegistration() {
|
||||
ConfigurableCommandRegistry registry = new ConfigurableCommandRegistry();
|
||||
ConfigurableCommandRegistry registry = new ConfigurableCommandRegistry(new DefaultShellContext());
|
||||
registry.register("foo", MethodTarget.of("toString", this, new Command.Help("some command")));
|
||||
assertThat(registry.listCommands()).containsKeys("foo");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDoubleRegistration() {
|
||||
ConfigurableCommandRegistry registry = new ConfigurableCommandRegistry();
|
||||
ConfigurableCommandRegistry registry = new ConfigurableCommandRegistry(new DefaultShellContext());
|
||||
registry.register("foo", MethodTarget.of("toString", this, new Command.Help("some command")));
|
||||
|
||||
assertThatThrownBy(() -> {
|
||||
|
||||
Reference in New Issue
Block a user