ListPlugins: Add support for filtering with prefix/substring/regex
Feature: Issue 6499 Change-Id: I7e54595dad56fc6a5e34bea55adee341077ad1fa
This commit is contained in:
parent
7f577a908c
commit
68d975b496
@ -110,6 +110,74 @@ Query the first plugin in the plugin list:
|
||||
}
|
||||
----
|
||||
|
||||
Prefix(p)::
|
||||
Limit the results to those plugins that start with the specified
|
||||
prefix.
|
||||
+
|
||||
The match is case sensitive. May not be used together with `m` or `r`.
|
||||
+
|
||||
List all plugins that start with `delete`:
|
||||
+
|
||||
.Request
|
||||
----
|
||||
GET /plugins/?p=delete HTTP/1.0
|
||||
----
|
||||
+
|
||||
.Response
|
||||
----
|
||||
HTTP/1.1 200 OK
|
||||
Content-Disposition: attachment
|
||||
Content-Type: application/json; charset=UTF-8
|
||||
|
||||
)]}'
|
||||
{
|
||||
"delete-project": {
|
||||
"id": "delete-project",
|
||||
"index_url": "plugins/delete-project/",
|
||||
"version": "2.9-SNAPSHOT"
|
||||
}
|
||||
}
|
||||
----
|
||||
+
|
||||
E.g. this feature can be used by suggestion client UI's to limit results.
|
||||
|
||||
Regex(r)::
|
||||
Limit the results to those plugins that match the specified regex.
|
||||
+
|
||||
Boundary matchers '^' and '$' are implicit. For example: the regex 'test.*' will
|
||||
match any plugins that start with 'test' and regex '.*test' will match any
|
||||
project that end with 'test'.
|
||||
+
|
||||
The match is case sensitive. May not be used together with `m` or `p`.
|
||||
+
|
||||
List all plugins that match regex `some.*plugin`:
|
||||
+
|
||||
.Request
|
||||
----
|
||||
GET /plugins/?r=some.*plugin HTTP/1.0
|
||||
----
|
||||
+
|
||||
.Response
|
||||
----
|
||||
HTTP/1.1 200 OK
|
||||
Content-Disposition: attachment
|
||||
Content-Type: application/json; charset=UTF-8
|
||||
|
||||
)]}'
|
||||
{
|
||||
"some-plugin": {
|
||||
"id": "some-plugin",
|
||||
"index_url": "plugins/some-plugin/",
|
||||
"version": "2.9-SNAPSHOT"
|
||||
},
|
||||
"some-other-plugin": {
|
||||
"id": "some-other-plugin",
|
||||
"index_url": "plugins/some-other-plugin/",
|
||||
"version": "2.9-SNAPSHOT"
|
||||
}
|
||||
}
|
||||
|
||||
----
|
||||
|
||||
Skip(S)::
|
||||
Skip the given number of plugins from the beginning of the list.
|
||||
@ -138,6 +206,33 @@ Query the second plugin in the plugin list:
|
||||
}
|
||||
----
|
||||
|
||||
Substring(m)::
|
||||
Limit the results to those plugins that match the specified substring.
|
||||
+
|
||||
The match is case insensitive. May not be used together with `r` or `p`.
|
||||
+
|
||||
List all plugins that match substring `project`:
|
||||
+
|
||||
.Request
|
||||
----
|
||||
GET /plugins/?m=project HTTP/1.0
|
||||
----
|
||||
+
|
||||
.Response
|
||||
----
|
||||
HTTP/1.1 200 OK
|
||||
Content-Disposition: attachment
|
||||
Content-Type: application/json; charset=UTF-8
|
||||
|
||||
)]}'
|
||||
{
|
||||
"delete-project": {
|
||||
"id": "delete-project",
|
||||
"index_url": "plugins/delete-project/",
|
||||
"version": "2.9-SNAPSHOT"
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
[[install-plugin]]
|
||||
=== Install Plugin
|
||||
|
@ -17,6 +17,7 @@ package com.google.gerrit.acceptance.api.plugin;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.gerrit.acceptance.AbstractDaemonTest;
|
||||
@ -27,6 +28,7 @@ import com.google.gerrit.extensions.api.plugins.PluginApi;
|
||||
import com.google.gerrit.extensions.api.plugins.Plugins.ListRequest;
|
||||
import com.google.gerrit.extensions.common.InstallPluginInput;
|
||||
import com.google.gerrit.extensions.common.PluginInfo;
|
||||
import com.google.gerrit.extensions.restapi.BadRequestException;
|
||||
import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
|
||||
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
|
||||
import com.google.gerrit.extensions.restapi.RestApiException;
|
||||
@ -63,6 +65,24 @@ public class PluginIT extends AbstractDaemonTest {
|
||||
// With pagination
|
||||
assertPlugins(list().start(1).limit(2).get(), PLUGINS.subList(1, 3));
|
||||
|
||||
// With prefix
|
||||
assertPlugins(list().prefix("plugin-b").get(), ImmutableList.of("plugin-b"));
|
||||
assertPlugins(list().prefix("PLUGIN-").get(), ImmutableList.of());
|
||||
|
||||
// With substring
|
||||
assertPlugins(list().substring("lugin-").get(), PLUGINS);
|
||||
assertPlugins(list().substring("lugin-").start(1).limit(2).get(), PLUGINS.subList(1, 3));
|
||||
|
||||
// With regex
|
||||
assertPlugins(list().regex(".*in-b").get(), ImmutableList.of("plugin-b"));
|
||||
assertPlugins(list().regex("plugin-.*").get(), PLUGINS);
|
||||
assertPlugins(list().regex("plugin-.*").start(1).limit(2).get(), PLUGINS.subList(1, 3));
|
||||
|
||||
// Invalid match combinations
|
||||
assertBadRequest(list().regex(".*in-b").substring("a"));
|
||||
assertBadRequest(list().regex(".*in-b").prefix("a"));
|
||||
assertBadRequest(list().substring(".*in-b").prefix("a"));
|
||||
|
||||
// Disable
|
||||
api = gApi.plugins().name("plugin-a");
|
||||
api.disable();
|
||||
@ -99,4 +119,13 @@ public class PluginIT extends AbstractDaemonTest {
|
||||
List<String> _actual = actual.stream().map(p -> p.id).collect(toList());
|
||||
assertThat(_actual).containsExactlyElementsIn(expected);
|
||||
}
|
||||
|
||||
private void assertBadRequest(ListRequest req) throws Exception {
|
||||
try {
|
||||
req.get();
|
||||
fail("Expected BadRequestException");
|
||||
} catch (BadRequestException e) {
|
||||
// Expected
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -35,6 +35,9 @@ public interface Plugins {
|
||||
private boolean all;
|
||||
private int limit;
|
||||
private int start;
|
||||
private String substring;
|
||||
private String prefix;
|
||||
private String regex;
|
||||
|
||||
public List<PluginInfo> get() throws RestApiException {
|
||||
Map<String, PluginInfo> map = getAsMap();
|
||||
@ -73,6 +76,33 @@ public interface Plugins {
|
||||
public int getStart() {
|
||||
return start;
|
||||
}
|
||||
|
||||
public ListRequest substring(String substring) {
|
||||
this.substring = substring;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getSubstring() {
|
||||
return substring;
|
||||
}
|
||||
|
||||
public ListRequest prefix(String prefix) {
|
||||
this.prefix = prefix;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getPrefix() {
|
||||
return prefix;
|
||||
}
|
||||
|
||||
public ListRequest regex(String regex) {
|
||||
this.regex = regex;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getRegex() {
|
||||
return regex;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -63,6 +63,9 @@ public class PluginsImpl implements Plugins {
|
||||
list.setAll(this.getAll());
|
||||
list.setStart(this.getStart());
|
||||
list.setLimit(this.getLimit());
|
||||
list.setMatchPrefix(this.getPrefix());
|
||||
list.setMatchSubstring(this.getSubstring());
|
||||
list.setMatchRegex(this.getRegex());
|
||||
return list.apply();
|
||||
}
|
||||
};
|
||||
|
@ -23,6 +23,7 @@ import com.google.gerrit.common.Nullable;
|
||||
import com.google.gerrit.common.data.GlobalCapability;
|
||||
import com.google.gerrit.extensions.annotations.RequiresCapability;
|
||||
import com.google.gerrit.extensions.common.PluginInfo;
|
||||
import com.google.gerrit.extensions.restapi.BadRequestException;
|
||||
import com.google.gerrit.extensions.restapi.RestReadView;
|
||||
import com.google.gerrit.extensions.restapi.TopLevelResource;
|
||||
import com.google.gerrit.extensions.restapi.Url;
|
||||
@ -31,9 +32,11 @@ import com.google.gson.reflect.TypeToken;
|
||||
import com.google.inject.Inject;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.SortedMap;
|
||||
import java.util.TreeMap;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Stream;
|
||||
import org.kohsuke.args4j.Option;
|
||||
|
||||
@ -45,6 +48,9 @@ public class ListPlugins implements RestReadView<TopLevelResource> {
|
||||
private boolean all;
|
||||
private int limit;
|
||||
private int start;
|
||||
private String matchPrefix;
|
||||
private String matchSubstring;
|
||||
private String matchRegex;
|
||||
|
||||
@Deprecated
|
||||
@Option(name = "--format", usage = "(deprecated) output format")
|
||||
@ -79,6 +85,31 @@ public class ListPlugins implements RestReadView<TopLevelResource> {
|
||||
this.start = start;
|
||||
}
|
||||
|
||||
@Option(
|
||||
name = "--prefix",
|
||||
aliases = {"-p"},
|
||||
metaVar = "PREFIX",
|
||||
usage = "match plugin prefix"
|
||||
)
|
||||
public void setMatchPrefix(String matchPrefix) {
|
||||
this.matchPrefix = matchPrefix;
|
||||
}
|
||||
|
||||
@Option(
|
||||
name = "--match",
|
||||
aliases = {"-m"},
|
||||
metaVar = "MATCH",
|
||||
usage = "match plugin substring"
|
||||
)
|
||||
public void setMatchSubstring(String matchSubstring) {
|
||||
this.matchSubstring = matchSubstring;
|
||||
}
|
||||
|
||||
@Option(name = "-r", metaVar = "REGEX", usage = "match plugin regex")
|
||||
public void setMatchRegex(String matchRegex) {
|
||||
this.matchRegex = matchRegex;
|
||||
}
|
||||
|
||||
@Inject
|
||||
protected ListPlugins(PluginLoader pluginLoader) {
|
||||
this.pluginLoader = pluginLoader;
|
||||
@ -94,20 +125,33 @@ public class ListPlugins implements RestReadView<TopLevelResource> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object apply(TopLevelResource resource) {
|
||||
public Object apply(TopLevelResource resource) throws BadRequestException {
|
||||
format = OutputFormat.JSON;
|
||||
return display(null);
|
||||
}
|
||||
|
||||
public SortedMap<String, PluginInfo> apply() {
|
||||
public SortedMap<String, PluginInfo> apply() throws BadRequestException {
|
||||
format = OutputFormat.JSON;
|
||||
return display(null);
|
||||
}
|
||||
|
||||
public SortedMap<String, PluginInfo> display(@Nullable PrintWriter stdout) {
|
||||
public SortedMap<String, PluginInfo> display(@Nullable PrintWriter stdout)
|
||||
throws BadRequestException {
|
||||
SortedMap<String, PluginInfo> output = new TreeMap<>();
|
||||
Stream<Plugin> s =
|
||||
Streams.stream(pluginLoader.getPlugins(all)).sorted(comparing(Plugin::getName));
|
||||
Stream<Plugin> s = Streams.stream(pluginLoader.getPlugins(all));
|
||||
if (matchPrefix != null) {
|
||||
checkMatchOptions(matchSubstring == null && matchRegex == null);
|
||||
s = s.filter(p -> p.getName().startsWith(matchPrefix));
|
||||
} else if (matchSubstring != null) {
|
||||
checkMatchOptions(matchPrefix == null && matchRegex == null);
|
||||
String substring = matchSubstring.toLowerCase(Locale.US);
|
||||
s = s.filter(p -> p.getName().toLowerCase(Locale.US).contains(substring));
|
||||
} else if (matchRegex != null) {
|
||||
checkMatchOptions(matchPrefix == null && matchSubstring == null);
|
||||
Pattern pattern = Pattern.compile(matchRegex);
|
||||
s = s.filter(p -> pattern.matcher(p.getName()).matches());
|
||||
}
|
||||
s = s.sorted(comparing(Plugin::getName));
|
||||
if (start > 0) {
|
||||
s = s.skip(start);
|
||||
}
|
||||
@ -148,6 +192,12 @@ public class ListPlugins implements RestReadView<TopLevelResource> {
|
||||
return null;
|
||||
}
|
||||
|
||||
private void checkMatchOptions(boolean cond) throws BadRequestException {
|
||||
if (!cond) {
|
||||
throw new BadRequestException("specify exactly one of p/m/r");
|
||||
}
|
||||
}
|
||||
|
||||
public static PluginInfo toPluginInfo(Plugin p) {
|
||||
String id;
|
||||
String version;
|
||||
|
Loading…
Reference in New Issue
Block a user