Support hidden commands
- CommandRegistration now has a structure to define it beind hidden - Modify relevant parts to filter out hidden commands - Essentially command is hidden from all other than command execution - Sample in e2e tests - Fixes #416
This commit is contained in:
@@ -18,7 +18,9 @@ package org.springframework.shell;
|
||||
import java.lang.reflect.UndeclaredThrowableException;
|
||||
import java.nio.channels.ClosedByInterruptException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -196,7 +198,7 @@ public class Shell {
|
||||
|
||||
List<String> words = input.words();
|
||||
String line = words.stream().collect(Collectors.joining(" ")).trim();
|
||||
String command = findLongestCommand(line);
|
||||
String command = findLongestCommand(line, false);
|
||||
|
||||
if (command == null) {
|
||||
return new CommandNotFound(words);
|
||||
@@ -349,7 +351,7 @@ public class Shell {
|
||||
List<CompletionProposal> candidates = new ArrayList<>();
|
||||
candidates.addAll(commandsStartingWith(prefix));
|
||||
|
||||
String best = findLongestCommand(prefix);
|
||||
String best = findLongestCommand(prefix, true);
|
||||
if (best != null) {
|
||||
context = context.drop(best.split(" ").length);
|
||||
CommandRegistration registration = commandRegistry.getRegistrations().get(best);
|
||||
@@ -434,7 +436,7 @@ public class Shell {
|
||||
// Workaround for https://github.com/spring-projects/spring-shell/issues/150
|
||||
// (sadly, this ties this class to JLine somehow)
|
||||
int lastWordStart = prefix.lastIndexOf(' ') + 1;
|
||||
return commandRegistry.getRegistrations().entrySet().stream()
|
||||
return Utils.removeHiddenCommands(commandRegistry.getRegistrations()).entrySet().stream()
|
||||
.filter(e -> e.getKey().startsWith(prefix))
|
||||
.map(e -> {
|
||||
String c = e.getKey();
|
||||
@@ -456,8 +458,12 @@ public class Shell {
|
||||
*
|
||||
* @return a valid command name, or {@literal null} if none matched
|
||||
*/
|
||||
private String findLongestCommand(String prefix) {
|
||||
String result = commandRegistry.getRegistrations().keySet().stream()
|
||||
private String findLongestCommand(String prefix, boolean filterHidden) {
|
||||
Map<String, CommandRegistration> registrations = commandRegistry.getRegistrations();
|
||||
if (filterHidden) {
|
||||
registrations = Utils.removeHiddenCommands(registrations);
|
||||
}
|
||||
String result = registrations.keySet().stream()
|
||||
.filter(command -> prefix.equals(command) || prefix.startsWith(command + " "))
|
||||
.reduce("", (c1, c2) -> c1.length() > c2.length() ? c1 : c2);
|
||||
return "".equals(result) ? null : result;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2015-2021 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.
|
||||
@@ -22,7 +22,9 @@ import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Parameter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
@@ -34,6 +36,7 @@ import jakarta.validation.ValidatorFactory;
|
||||
|
||||
import org.springframework.core.DefaultParameterNameDiscoverer;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.shell.command.CommandRegistration;
|
||||
|
||||
/**
|
||||
* Some text utilities.
|
||||
@@ -166,4 +169,21 @@ public class Utils {
|
||||
|
||||
return split;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a map of command registrations and removes hidden commands from it.
|
||||
*
|
||||
* @param registrations a command registrations
|
||||
* @return same map with removed hidden commands
|
||||
*/
|
||||
public static Map<String, CommandRegistration> removeHiddenCommands(Map<String, CommandRegistration> registrations) {
|
||||
Iterator<Map.Entry<String, CommandRegistration>> iter = registrations.entrySet().iterator();
|
||||
while (iter.hasNext()) {
|
||||
Map.Entry<String, CommandRegistration> entry = iter.next();
|
||||
if (entry.getValue().isHidden()) {
|
||||
iter.remove();
|
||||
}
|
||||
}
|
||||
return registrations;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,6 +65,13 @@ public interface CommandRegistration {
|
||||
*/
|
||||
String getGroup();
|
||||
|
||||
/**
|
||||
* Returns if command is hidden.
|
||||
*
|
||||
* @return true if command is hidden
|
||||
*/
|
||||
boolean isHidden();
|
||||
|
||||
/**
|
||||
* Get description for a command.
|
||||
*
|
||||
@@ -516,6 +523,22 @@ public interface CommandRegistration {
|
||||
*/
|
||||
Builder group(String group);
|
||||
|
||||
/**
|
||||
* Define a command to be hidden.
|
||||
*
|
||||
* @return builder for chaining
|
||||
* @see #hidden(boolean)
|
||||
*/
|
||||
Builder hidden();
|
||||
|
||||
/**
|
||||
* Define a command to be hidden by a given flag.
|
||||
*
|
||||
* @param hidden the hidden flag
|
||||
* @return builder for chaining
|
||||
*/
|
||||
Builder hidden(boolean hidden);
|
||||
|
||||
/**
|
||||
* Define an option what this command should user for. Can be used multiple
|
||||
* times.
|
||||
@@ -866,6 +889,7 @@ public interface CommandRegistration {
|
||||
private String command;
|
||||
private InteractionMode interactionMode;
|
||||
private String group;
|
||||
private boolean hidden;
|
||||
private String description;
|
||||
private Supplier<Availability> availability;
|
||||
private List<DefaultOptionSpec> optionSpecs;
|
||||
@@ -875,12 +899,13 @@ public interface CommandRegistration {
|
||||
private DefaultErrorHandlingSpec errorHandlingSpec;
|
||||
|
||||
public DefaultCommandRegistration(String[] commands, InteractionMode interactionMode, String group,
|
||||
String description, Supplier<Availability> availability, List<DefaultOptionSpec> optionSpecs,
|
||||
DefaultTargetSpec targetSpec, List<DefaultAliasSpec> aliasSpecs, DefaultExitCodeSpec exitCodeSpec,
|
||||
DefaultErrorHandlingSpec errorHandlingSpec) {
|
||||
boolean hidden, String description, Supplier<Availability> availability,
|
||||
List<DefaultOptionSpec> optionSpecs, DefaultTargetSpec targetSpec, List<DefaultAliasSpec> aliasSpecs,
|
||||
DefaultExitCodeSpec exitCodeSpec, DefaultErrorHandlingSpec errorHandlingSpec) {
|
||||
this.command = commandArrayToName(commands);
|
||||
this.interactionMode = interactionMode;
|
||||
this.group = group;
|
||||
this.hidden = hidden;
|
||||
this.description = description;
|
||||
this.availability = availability;
|
||||
this.optionSpecs = optionSpecs;
|
||||
@@ -905,6 +930,11 @@ public interface CommandRegistration {
|
||||
return group;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isHidden() {
|
||||
return hidden;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return description;
|
||||
@@ -985,6 +1015,7 @@ public interface CommandRegistration {
|
||||
private String[] commands;
|
||||
private InteractionMode interactionMode = InteractionMode.ALL;
|
||||
private String group;
|
||||
private boolean hidden;
|
||||
private String description;
|
||||
private Supplier<Availability> availability;
|
||||
private List<DefaultOptionSpec> optionSpecs = new ArrayList<>();
|
||||
@@ -1023,6 +1054,17 @@ public interface CommandRegistration {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder hidden() {
|
||||
return hidden(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder hidden(boolean hidden) {
|
||||
this.hidden = hidden;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder availability(Supplier<Availability> availability) {
|
||||
this.availability = availability;
|
||||
@@ -1069,7 +1111,7 @@ public interface CommandRegistration {
|
||||
Assert.notNull(commands, "command cannot be empty");
|
||||
Assert.notNull(targetSpec, "target cannot be empty");
|
||||
Assert.state(!(targetSpec.bean != null && targetSpec.function != null), "only one target can exist");
|
||||
return new DefaultCommandRegistration(commands, interactionMode, group, description, availability,
|
||||
return new DefaultCommandRegistration(commands, interactionMode, group, hidden, description, availability,
|
||||
optionSpecs, targetSpec, aliasSpecs, exitCodeSpec, errorHandlingSpec);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -485,4 +485,42 @@ public class CommandRegistrationTests extends AbstractCommandTests {
|
||||
.build();
|
||||
assertThat(registration.getExceptionResolvers()).hasSize(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHidden() {
|
||||
CommandRegistration registration = CommandRegistration.builder()
|
||||
.command("command1")
|
||||
.withTarget()
|
||||
.function(function1)
|
||||
.and()
|
||||
.build();
|
||||
assertThat(registration.isHidden()).isFalse();
|
||||
|
||||
registration = CommandRegistration.builder()
|
||||
.command("command1")
|
||||
.hidden()
|
||||
.withTarget()
|
||||
.function(function1)
|
||||
.and()
|
||||
.build();
|
||||
assertThat(registration.isHidden()).isTrue();
|
||||
|
||||
registration = CommandRegistration.builder()
|
||||
.command("command1")
|
||||
.hidden(false)
|
||||
.withTarget()
|
||||
.function(function1)
|
||||
.and()
|
||||
.build();
|
||||
assertThat(registration.isHidden()).isFalse();
|
||||
|
||||
registration = CommandRegistration.builder()
|
||||
.command("command1")
|
||||
.hidden(true)
|
||||
.withTarget()
|
||||
.function(function1)
|
||||
.and()
|
||||
.build();
|
||||
assertThat(registration.isHidden()).isTrue();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user