Add support for regex in project list screen
Add support for regex in project list screen filter. If filter start with '^', then the filter will be interpreted as a regex. Feature: Issue 2751 Change-Id: I1cf8a0f9fc18a6e286a2a26c71be77f1d4f16741
This commit is contained in:
parent
91b7f7ce39
commit
b8aae411a8
@ -175,6 +175,38 @@ List all projects that start with `platform/`:
|
|||||||
+
|
+
|
||||||
E.g. this feature can be used by suggestion client UI's to limit results.
|
E.g. this feature can be used by suggestion client UI's to limit results.
|
||||||
|
|
||||||
|
Regex(r)::
|
||||||
|
Limit the results to those projects that match the specified regex.
|
||||||
|
+
|
||||||
|
Boundary matchers '^' and '$' are implicit. For example: the regex 'test.*' will
|
||||||
|
match any projects that start with 'test' and regex '.*test' will match any
|
||||||
|
project that end with 'test'.
|
||||||
|
+
|
||||||
|
List all projects that match regex `test.*project`:
|
||||||
|
+
|
||||||
|
.Request
|
||||||
|
----
|
||||||
|
GET /projects/?r=test.*project HTTP/1.0
|
||||||
|
----
|
||||||
|
+
|
||||||
|
.Response
|
||||||
|
----
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
Content-Disposition: attachment
|
||||||
|
Content-Type: application/json;charset=UTF-8
|
||||||
|
|
||||||
|
)]}'
|
||||||
|
{
|
||||||
|
"test/some-project": {
|
||||||
|
"id": "test%2Fsome-project",
|
||||||
|
},
|
||||||
|
"test/some-other-project": {
|
||||||
|
"id": "test%2Fsome-other-project",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
Skip(S)::
|
Skip(S)::
|
||||||
Skip the given number of projects from the beginning of the list.
|
Skip the given number of projects from the beginning of the list.
|
||||||
+
|
+
|
||||||
|
@ -128,6 +128,31 @@ public class ListProjectsIT extends AbstractDaemonTest {
|
|||||||
result.values());
|
result.values());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void listProjectsWithRegex() throws IOException, JSchException {
|
||||||
|
Project.NameKey someProject = new Project.NameKey("some-project");
|
||||||
|
createProject(sshSession, someProject.get());
|
||||||
|
Project.NameKey someOtherProject =
|
||||||
|
new Project.NameKey("some-other-project");
|
||||||
|
createProject(sshSession, someOtherProject.get());
|
||||||
|
Project.NameKey projectAwesome = new Project.NameKey("project-awesome");
|
||||||
|
createProject(sshSession, projectAwesome.get());
|
||||||
|
|
||||||
|
RestResponse r = GET("/projects/?r=.*some");
|
||||||
|
assertEquals(HttpStatus.SC_OK, r.getStatusCode());
|
||||||
|
Map<String, ProjectInfo> result = toProjectInfoMap(r);
|
||||||
|
assertProjects(Arrays.asList(projectAwesome), result.values());
|
||||||
|
|
||||||
|
r = GET("/projects/?r=[.*some");
|
||||||
|
assertEquals(HttpStatus.SC_BAD_REQUEST, r.getStatusCode());
|
||||||
|
|
||||||
|
r = GET("/projects/?r=.*");
|
||||||
|
assertEquals(HttpStatus.SC_OK, r.getStatusCode());
|
||||||
|
result = toProjectInfoMap(r);
|
||||||
|
assertProjects(Arrays.asList(someProject, someOtherProject, projectAwesome,
|
||||||
|
project, allUsers), result.values());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void listProjectsWithSkip() throws IOException, JSchException {
|
public void listProjectsWithSkip() throws IOException, JSchException {
|
||||||
for (int i = 0; i < 5; i++) {
|
for (int i = 0; i < 5; i++) {
|
||||||
|
@ -56,7 +56,11 @@ public class ProjectMap extends NativeMap<ProjectInfo> {
|
|||||||
public static void match(String match, int limit, int start, AsyncCallback<ProjectMap> cb) {
|
public static void match(String match, int limit, int start, AsyncCallback<ProjectMap> cb) {
|
||||||
RestApi call = new RestApi("/projects/");
|
RestApi call = new RestApi("/projects/");
|
||||||
if (match != null) {
|
if (match != null) {
|
||||||
call.addParameter("m", match);
|
if (match.startsWith("^")) {
|
||||||
|
call.addParameter("r", match);
|
||||||
|
} else {
|
||||||
|
call.addParameter("m", match);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (limit > 0) {
|
if (limit > 0) {
|
||||||
call.addParameter("n", limit);
|
call.addParameter("n", limit);
|
||||||
|
@ -23,6 +23,7 @@ import com.google.gerrit.common.data.GroupReference;
|
|||||||
import com.google.gerrit.common.errors.NoSuchGroupException;
|
import com.google.gerrit.common.errors.NoSuchGroupException;
|
||||||
import com.google.gerrit.extensions.common.ProjectInfo;
|
import com.google.gerrit.extensions.common.ProjectInfo;
|
||||||
import com.google.gerrit.extensions.common.WebLinkInfo;
|
import com.google.gerrit.extensions.common.WebLinkInfo;
|
||||||
|
import com.google.gerrit.extensions.restapi.BadRequestException;
|
||||||
import com.google.gerrit.extensions.restapi.BinaryResult;
|
import com.google.gerrit.extensions.restapi.BinaryResult;
|
||||||
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;
|
||||||
@ -42,6 +43,9 @@ import com.google.gson.reflect.TypeToken;
|
|||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.Provider;
|
import com.google.inject.Provider;
|
||||||
|
|
||||||
|
import dk.brics.automaton.RegExp;
|
||||||
|
import dk.brics.automaton.RunAutomaton;
|
||||||
|
|
||||||
import org.eclipse.jgit.errors.RepositoryNotFoundException;
|
import org.eclipse.jgit.errors.RepositoryNotFoundException;
|
||||||
import org.eclipse.jgit.lib.Constants;
|
import org.eclipse.jgit.lib.Constants;
|
||||||
import org.eclipse.jgit.lib.Ref;
|
import org.eclipse.jgit.lib.Ref;
|
||||||
@ -163,6 +167,11 @@ public class ListProjects implements RestReadView<TopLevelResource> {
|
|||||||
this.matchSubstring = matchSubstring;
|
this.matchSubstring = matchSubstring;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Option(name = "-r", metaVar = "REGEX", usage = "match project regex")
|
||||||
|
public void setMatchRegex(String matchRegex) {
|
||||||
|
this.matchRegex = matchRegex;
|
||||||
|
}
|
||||||
|
|
||||||
@Option(name = "--has-acl-for", metaVar = "GROUP", usage =
|
@Option(name = "--has-acl-for", metaVar = "GROUP", usage =
|
||||||
"displays only projects on which access rights for this group are directly assigned")
|
"displays only projects on which access rights for this group are directly assigned")
|
||||||
public void setGroupUuid(AccountGroup.UUID groupUuid) {
|
public void setGroupUuid(AccountGroup.UUID groupUuid) {
|
||||||
@ -178,6 +187,7 @@ public class ListProjects implements RestReadView<TopLevelResource> {
|
|||||||
private int start;
|
private int start;
|
||||||
private String matchPrefix;
|
private String matchPrefix;
|
||||||
private String matchSubstring;
|
private String matchSubstring;
|
||||||
|
private String matchRegex;
|
||||||
private AccountGroup.UUID groupUuid;
|
private AccountGroup.UUID groupUuid;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
@ -216,7 +226,7 @@ public class ListProjects implements RestReadView<TopLevelResource> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object apply(TopLevelResource resource) {
|
public Object apply(TopLevelResource resource) throws BadRequestException {
|
||||||
if (format == OutputFormat.TEXT) {
|
if (format == OutputFormat.TEXT) {
|
||||||
ByteArrayOutputStream buf = new ByteArrayOutputStream();
|
ByteArrayOutputStream buf = new ByteArrayOutputStream();
|
||||||
display(buf);
|
display(buf);
|
||||||
@ -227,12 +237,13 @@ public class ListProjects implements RestReadView<TopLevelResource> {
|
|||||||
return apply();
|
return apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, ProjectInfo> apply() {
|
public Map<String, ProjectInfo> apply() throws BadRequestException {
|
||||||
format = OutputFormat.JSON;
|
format = OutputFormat.JSON;
|
||||||
return display(null);
|
return display(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, ProjectInfo> display(OutputStream displayOutputStream) {
|
public Map<String, ProjectInfo> display(OutputStream displayOutputStream)
|
||||||
|
throws BadRequestException {
|
||||||
PrintWriter stdout = null;
|
PrintWriter stdout = null;
|
||||||
if (displayOutputStream != null) {
|
if (displayOutputStream != null) {
|
||||||
try {
|
try {
|
||||||
@ -436,7 +447,7 @@ public class ListProjects implements RestReadView<TopLevelResource> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Iterable<Project.NameKey> scan() {
|
private Iterable<Project.NameKey> scan() throws BadRequestException {
|
||||||
if (matchPrefix != null) {
|
if (matchPrefix != null) {
|
||||||
return projectCache.byName(matchPrefix);
|
return projectCache.byName(matchPrefix);
|
||||||
} else if (matchSubstring != null) {
|
} else if (matchSubstring != null) {
|
||||||
@ -447,6 +458,28 @@ public class ListProjects implements RestReadView<TopLevelResource> {
|
|||||||
.contains(matchSubstring.toLowerCase(Locale.US));
|
.contains(matchSubstring.toLowerCase(Locale.US));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
} else if (matchRegex != null) {
|
||||||
|
if (matchRegex.startsWith("^")) {
|
||||||
|
matchRegex = matchRegex.substring(1);
|
||||||
|
if (matchRegex.endsWith("$") && !matchRegex.endsWith("\\$")) {
|
||||||
|
matchRegex = matchRegex.substring(0, matchRegex.length() - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (matchRegex.equals(".*")) {
|
||||||
|
return projectCache.all();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
final RunAutomaton a =
|
||||||
|
new RunAutomaton(new RegExp(matchRegex).toAutomaton());
|
||||||
|
return Iterables.filter(projectCache.all(),
|
||||||
|
new Predicate<Project.NameKey>() {
|
||||||
|
public boolean apply(Project.NameKey in) {
|
||||||
|
return a.run(in.get());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
throw new BadRequestException(e.getMessage());
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return projectCache.all();
|
return projectCache.all();
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user