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(S)::
|
||||||
Skip the given number of plugins from the beginning of the list.
|
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]]
|
||||||
=== 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 com.google.common.truth.Truth.assertThat;
|
||||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||||
import static java.util.stream.Collectors.toList;
|
import static java.util.stream.Collectors.toList;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.gerrit.acceptance.AbstractDaemonTest;
|
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.api.plugins.Plugins.ListRequest;
|
||||||
import com.google.gerrit.extensions.common.InstallPluginInput;
|
import com.google.gerrit.extensions.common.InstallPluginInput;
|
||||||
import com.google.gerrit.extensions.common.PluginInfo;
|
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.MethodNotAllowedException;
|
||||||
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
|
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
|
||||||
import com.google.gerrit.extensions.restapi.RestApiException;
|
import com.google.gerrit.extensions.restapi.RestApiException;
|
||||||
@ -63,6 +65,24 @@ public class PluginIT extends AbstractDaemonTest {
|
|||||||
// With pagination
|
// With pagination
|
||||||
assertPlugins(list().start(1).limit(2).get(), PLUGINS.subList(1, 3));
|
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
|
// Disable
|
||||||
api = gApi.plugins().name("plugin-a");
|
api = gApi.plugins().name("plugin-a");
|
||||||
api.disable();
|
api.disable();
|
||||||
@ -99,4 +119,13 @@ public class PluginIT extends AbstractDaemonTest {
|
|||||||
List<String> _actual = actual.stream().map(p -> p.id).collect(toList());
|
List<String> _actual = actual.stream().map(p -> p.id).collect(toList());
|
||||||
assertThat(_actual).containsExactlyElementsIn(expected);
|
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 boolean all;
|
||||||
private int limit;
|
private int limit;
|
||||||
private int start;
|
private int start;
|
||||||
|
private String substring;
|
||||||
|
private String prefix;
|
||||||
|
private String regex;
|
||||||
|
|
||||||
public List<PluginInfo> get() throws RestApiException {
|
public List<PluginInfo> get() throws RestApiException {
|
||||||
Map<String, PluginInfo> map = getAsMap();
|
Map<String, PluginInfo> map = getAsMap();
|
||||||
@ -73,6 +76,33 @@ public interface Plugins {
|
|||||||
public int getStart() {
|
public int getStart() {
|
||||||
return start;
|
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.setAll(this.getAll());
|
||||||
list.setStart(this.getStart());
|
list.setStart(this.getStart());
|
||||||
list.setLimit(this.getLimit());
|
list.setLimit(this.getLimit());
|
||||||
|
list.setMatchPrefix(this.getPrefix());
|
||||||
|
list.setMatchSubstring(this.getSubstring());
|
||||||
|
list.setMatchRegex(this.getRegex());
|
||||||
return list.apply();
|
return list.apply();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -23,6 +23,7 @@ import com.google.gerrit.common.Nullable;
|
|||||||
import com.google.gerrit.common.data.GlobalCapability;
|
import com.google.gerrit.common.data.GlobalCapability;
|
||||||
import com.google.gerrit.extensions.annotations.RequiresCapability;
|
import com.google.gerrit.extensions.annotations.RequiresCapability;
|
||||||
import com.google.gerrit.extensions.common.PluginInfo;
|
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.RestReadView;
|
||||||
import com.google.gerrit.extensions.restapi.TopLevelResource;
|
import com.google.gerrit.extensions.restapi.TopLevelResource;
|
||||||
import com.google.gerrit.extensions.restapi.Url;
|
import com.google.gerrit.extensions.restapi.Url;
|
||||||
@ -31,9 +32,11 @@ import com.google.gson.reflect.TypeToken;
|
|||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.SortedMap;
|
import java.util.SortedMap;
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
import org.kohsuke.args4j.Option;
|
import org.kohsuke.args4j.Option;
|
||||||
|
|
||||||
@ -45,6 +48,9 @@ public class ListPlugins implements RestReadView<TopLevelResource> {
|
|||||||
private boolean all;
|
private boolean all;
|
||||||
private int limit;
|
private int limit;
|
||||||
private int start;
|
private int start;
|
||||||
|
private String matchPrefix;
|
||||||
|
private String matchSubstring;
|
||||||
|
private String matchRegex;
|
||||||
|
|
||||||
@Deprecated
|
@Deprecated
|
||||||
@Option(name = "--format", usage = "(deprecated) output format")
|
@Option(name = "--format", usage = "(deprecated) output format")
|
||||||
@ -79,6 +85,31 @@ public class ListPlugins implements RestReadView<TopLevelResource> {
|
|||||||
this.start = start;
|
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
|
@Inject
|
||||||
protected ListPlugins(PluginLoader pluginLoader) {
|
protected ListPlugins(PluginLoader pluginLoader) {
|
||||||
this.pluginLoader = pluginLoader;
|
this.pluginLoader = pluginLoader;
|
||||||
@ -94,20 +125,33 @@ public class ListPlugins implements RestReadView<TopLevelResource> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object apply(TopLevelResource resource) {
|
public Object apply(TopLevelResource resource) throws BadRequestException {
|
||||||
format = OutputFormat.JSON;
|
format = OutputFormat.JSON;
|
||||||
return display(null);
|
return display(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SortedMap<String, PluginInfo> apply() {
|
public SortedMap<String, PluginInfo> apply() throws BadRequestException {
|
||||||
format = OutputFormat.JSON;
|
format = OutputFormat.JSON;
|
||||||
return display(null);
|
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<>();
|
SortedMap<String, PluginInfo> output = new TreeMap<>();
|
||||||
Stream<Plugin> s =
|
Stream<Plugin> s = Streams.stream(pluginLoader.getPlugins(all));
|
||||||
Streams.stream(pluginLoader.getPlugins(all)).sorted(comparing(Plugin::getName));
|
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) {
|
if (start > 0) {
|
||||||
s = s.skip(start);
|
s = s.skip(start);
|
||||||
}
|
}
|
||||||
@ -148,6 +192,12 @@ public class ListPlugins implements RestReadView<TopLevelResource> {
|
|||||||
return null;
|
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) {
|
public static PluginInfo toPluginInfo(Plugin p) {
|
||||||
String id;
|
String id;
|
||||||
String version;
|
String version;
|
||||||
|
Loading…
Reference in New Issue
Block a user