Merge pull request #18942 from jimferenczi/did_you_mean_plugin

Add did-you-mean for plugin cli
This commit is contained in:
Jim Ferenczi 2016-06-17 14:22:49 +02:00 committed by GitHub
commit d72e79159e
2 changed files with 40 additions and 1 deletions

View File

@ -45,11 +45,14 @@ import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.TreeSet;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import joptsimple.OptionSet;
import joptsimple.OptionSpec;
import org.apache.lucene.search.spell.LevensteinDistance;
import org.apache.lucene.util.CollectionUtil;
import org.apache.lucene.util.IOUtils;
import org.elasticsearch.Version;
import org.elasticsearch.bootstrap.JarHell;
@ -57,6 +60,7 @@ import org.elasticsearch.cli.ExitCodes;
import org.elasticsearch.cli.SettingCommand;
import org.elasticsearch.cli.Terminal;
import org.elasticsearch.cli.UserError;
import org.elasticsearch.common.collect.Tuple;
import org.elasticsearch.common.hash.MessageDigests;
import org.elasticsearch.common.io.FileSystemUtils;
import org.elasticsearch.common.settings.Settings;
@ -239,12 +243,31 @@ class InstallPluginCommand extends SettingCommand {
// fall back to plain old URL
if (pluginId.contains(":/") == false) {
// definitely not a valid url, so assume it is a plugin name
throw new UserError(ExitCodes.USAGE, "Unknown plugin " + pluginId);
List<String> plugins = checkMisspelledPlugin(pluginId);
String msg = "Unknown plugin " + pluginId;
if (plugins.isEmpty() == false) {
msg += ", did you mean " + (plugins.size() == 1 ? "[" + plugins.get(0) + "]": "any of " + plugins.toString()) + "?";
}
throw new UserError(ExitCodes.USAGE, msg);
}
terminal.println("-> Downloading " + URLDecoder.decode(pluginId, "UTF-8"));
return downloadZip(terminal, pluginId, tmpDir);
}
/** Returns all the official plugin names that look similar to pluginId. **/
private List<String> checkMisspelledPlugin(String pluginId) {
LevensteinDistance ld = new LevensteinDistance();
List<Tuple<Float, String>> scoredKeys = new ArrayList<>();
for (String officialPlugin : OFFICIAL_PLUGINS) {
float distance = ld.getDistance(pluginId, officialPlugin);
if (distance > 0.7f) {
scoredKeys.add(new Tuple<>(distance, officialPlugin));
}
}
CollectionUtil.timSort(scoredKeys, (a, b) -> b.v1().compareTo(a.v1()));
return scoredKeys.stream().map((a) -> a.v2()).collect(Collectors.toList());
}
/** Downloads a zip from the url, into a temp file under the given temp dir. */
private Path downloadZip(Terminal terminal, String urlString, Path tmpDir) throws IOException {
terminal.println(VERBOSE, "Retrieving zip from " + urlString);

View File

@ -67,6 +67,7 @@ import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.Matchers.containsInAnyOrder;
@ -567,6 +568,21 @@ public class InstallPluginCommandTests extends ESTestCase {
assertTrue(terminal.getOutput(), terminal.getOutput().contains("x-pack"));
}
public void testInstallMisspelledOfficialPlugins() throws Exception {
Tuple<Path, Environment> env = createEnv(fs, temp);
UserError e = expectThrows(UserError.class, () -> installPlugin("xpack", env.v1()));
assertThat(e.getMessage(), containsString("Unknown plugin xpack, did you mean [x-pack]?"));
e = expectThrows(UserError.class, () -> installPlugin("analysis-smartnc", env.v1()));
assertThat(e.getMessage(), containsString("Unknown plugin analysis-smartnc, did you mean [analysis-smartcn]?"));
e = expectThrows(UserError.class, () -> installPlugin("repository", env.v1()));
assertThat(e.getMessage(), containsString("Unknown plugin repository, did you mean any of [repository-s3, repository-gcs]?"));
e = expectThrows(UserError.class, () -> installPlugin("unknown_plugin", env.v1()));
assertThat(e.getMessage(), containsString("Unknown plugin unknown_plugin"));
}
// TODO: test batch flag?
// TODO: test checksum (need maven/official below)
// TODO: test maven, official, and staging install...need tests with fixtures...