Add STATE field to ProjectIndex
This commit adds the ProjectState to the project index and makes it
queryable. V2 was added in this series, so adding a field does not
require creating V3.
Our post processors filter out hidden projects, so it doesn't make much
sense for users to query for them. As a future optimisation we can make
the default query exclude hidden projects by adding 'not state:hidden'
so that we have to do less post-filtering.
Change-Id: I98434e4b4ea232e257cca4b551fcaba21b3db656
diff --git a/Documentation/user-search-projects.txt b/Documentation/user-search-projects.txt
index ba20adb..11c1326 100644
--- a/Documentation/user-search-projects.txt
+++ b/Documentation/user-search-projects.txt
@@ -24,6 +24,11 @@
Matches projects whose description contains 'DESCRIPTION', using a
full-text search.
+[[state]]
+state:'STATE'::
++
+Matches project's state. Can be either 'active' or 'read-only'.
+
== Magical Operators
[[is-visible]]
diff --git a/java/com/google/gerrit/index/project/ProjectField.java b/java/com/google/gerrit/index/project/ProjectField.java
index 041813c..5388253 100644
--- a/java/com/google/gerrit/index/project/ProjectField.java
+++ b/java/com/google/gerrit/index/project/ProjectField.java
@@ -45,6 +45,9 @@
public static final FieldDef<ProjectData, Iterable<String>> NAME_PART =
prefix("name_part").buildRepeatable(p -> SchemaUtil.getNameParts(p.getProject().getName()));
+ public static final FieldDef<ProjectData, String> STATE =
+ exact("state").stored().build(p -> p.getProject().getState().name());
+
public static final FieldDef<ProjectData, Iterable<String>> ANCESTOR_NAME =
exact("ancestor_name").buildRepeatable(p -> p.getParentNames());
diff --git a/java/com/google/gerrit/index/project/ProjectSchemaDefinitions.java b/java/com/google/gerrit/index/project/ProjectSchemaDefinitions.java
index 4d212fb..07b5adb 100644
--- a/java/com/google/gerrit/index/project/ProjectSchemaDefinitions.java
+++ b/java/com/google/gerrit/index/project/ProjectSchemaDefinitions.java
@@ -30,7 +30,7 @@
ProjectField.NAME_PART,
ProjectField.ANCESTOR_NAME);
- static final Schema<ProjectData> V2 = schema(V1, ProjectField.REF_STATE);
+ static final Schema<ProjectData> V2 = schema(V1, ProjectField.STATE, ProjectField.REF_STATE);
public static final ProjectSchemaDefinitions INSTANCE = new ProjectSchemaDefinitions();
diff --git a/java/com/google/gerrit/server/query/project/ProjectPredicates.java b/java/com/google/gerrit/server/query/project/ProjectPredicates.java
index b4f56d4..2e406aa 100644
--- a/java/com/google/gerrit/server/query/project/ProjectPredicates.java
+++ b/java/com/google/gerrit/server/query/project/ProjectPredicates.java
@@ -14,6 +14,7 @@
package com.google.gerrit.server.query.project;
+import com.google.gerrit.extensions.client.ProjectState;
import com.google.gerrit.index.project.ProjectData;
import com.google.gerrit.index.project.ProjectField;
import com.google.gerrit.index.project.ProjectPredicate;
@@ -34,5 +35,9 @@
return new ProjectPredicate(ProjectField.DESCRIPTION, description);
}
+ public static Predicate<ProjectData> state(ProjectState state) {
+ return new ProjectPredicate(ProjectField.STATE, state.name());
+ }
+
private ProjectPredicates() {}
}
diff --git a/java/com/google/gerrit/server/query/project/ProjectQueryBuilder.java b/java/com/google/gerrit/server/query/project/ProjectQueryBuilder.java
index be7ea22..872d3e0 100644
--- a/java/com/google/gerrit/server/query/project/ProjectQueryBuilder.java
+++ b/java/com/google/gerrit/server/query/project/ProjectQueryBuilder.java
@@ -17,6 +17,7 @@
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.common.primitives.Ints;
+import com.google.gerrit.extensions.client.ProjectState;
import com.google.gerrit.index.project.ProjectData;
import com.google.gerrit.index.query.LimitPredicate;
import com.google.gerrit.index.query.Predicate;
@@ -60,6 +61,23 @@
return ProjectPredicates.description(description);
}
+ @Operator
+ public Predicate<ProjectData> state(String state) throws QueryParseException {
+ if (Strings.isNullOrEmpty(state)) {
+ throw error("state operator requires a value");
+ }
+ ProjectState parsedState;
+ try {
+ parsedState = ProjectState.valueOf(state.replace('-', '_').toUpperCase());
+ } catch (IllegalArgumentException e) {
+ throw error("state operator must be either 'active' or 'read-only'");
+ }
+ if (parsedState == ProjectState.HIDDEN) {
+ throw error("state operator must be either 'active' or 'read-only'");
+ }
+ return ProjectPredicates.state(parsedState);
+ }
+
@Override
protected Predicate<ProjectData> defaultField(String query) throws QueryParseException {
// Adapt the capacity of this list when adding more default predicates.
diff --git a/javatests/com/google/gerrit/server/query/project/AbstractQueryProjectsTest.java b/javatests/com/google/gerrit/server/query/project/AbstractQueryProjectsTest.java
index e34746c..2eec006 100644
--- a/javatests/com/google/gerrit/server/query/project/AbstractQueryProjectsTest.java
+++ b/javatests/com/google/gerrit/server/query/project/AbstractQueryProjectsTest.java
@@ -15,15 +15,21 @@
package com.google.gerrit.server.query.project;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.TruthJUnit.assume;
import static java.util.stream.Collectors.toList;
import com.google.common.base.CharMatcher;
import com.google.gerrit.extensions.api.GerritApi;
+import com.google.gerrit.extensions.api.projects.ConfigInput;
import com.google.gerrit.extensions.api.projects.ProjectInput;
import com.google.gerrit.extensions.api.projects.Projects.QueryRequest;
+import com.google.gerrit.extensions.client.ProjectState;
import com.google.gerrit.extensions.common.AccountInfo;
import com.google.gerrit.extensions.common.ProjectInfo;
import com.google.gerrit.extensions.restapi.BadRequestException;
+import com.google.gerrit.index.Schema;
+import com.google.gerrit.index.project.ProjectData;
+import com.google.gerrit.index.project.ProjectIndexCollection;
import com.google.gerrit.lifecycle.LifecycleManager;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.Project;
@@ -38,7 +44,6 @@
import com.google.gerrit.server.account.AccountsUpdate;
import com.google.gerrit.server.account.AuthRequest;
import com.google.gerrit.server.config.AllProjectsName;
-import com.google.gerrit.server.query.account.InternalAccountQuery;
import com.google.gerrit.server.schema.SchemaCreator;
import com.google.gerrit.server.util.ManualRequestContext;
import com.google.gerrit.server.util.OneOffRequestContext;
@@ -83,7 +88,7 @@
@Inject protected OneOffRequestContext oneOffRequestContext;
- @Inject protected InternalAccountQuery internalAccountQuery;
+ @Inject protected ProjectIndexCollection indexes;
@Inject protected AllProjectsName allProjects;
@@ -211,6 +216,30 @@
}
@Test
+ public void byState() throws Exception {
+ assume().that(getSchemaVersion() >= 2).isTrue();
+
+ ProjectInfo project1 = createProjectWithState(name("project1"), ProjectState.ACTIVE);
+ ProjectInfo project2 = createProjectWithState(name("project2"), ProjectState.READ_ONLY);
+ assertQuery("state:active", project1);
+ assertQuery("state:read-only", project2);
+ }
+
+ @Test
+ public void byState_emptyQuery() throws Exception {
+ exception.expect(BadRequestException.class);
+ exception.expectMessage("state operator requires a value");
+ assertQuery("state:\"\"");
+ }
+
+ @Test
+ public void byState_badQuery() throws Exception {
+ exception.expect(BadRequestException.class);
+ exception.expectMessage("state operator must be either 'active' or 'read-only'");
+ assertQuery("state:bla");
+ }
+
+ @Test
public void byDefaultField() throws Exception {
ProjectInfo project1 = createProject(name("foo-project"));
ProjectInfo project2 = createProject(name("project2"));
@@ -291,6 +320,14 @@
return gApi.projects().create(in).get();
}
+ protected ProjectInfo createProjectWithState(String name, ProjectState state) throws Exception {
+ ProjectInfo info = createProject(name);
+ ConfigInput config = new ConfigInput();
+ config.state = state;
+ gApi.projects().name(info.name).config(config);
+ return info;
+ }
+
protected ProjectInfo getProject(Project.NameKey nameKey) throws Exception {
return gApi.projects().name(nameKey.get()).get();
}
@@ -354,6 +391,14 @@
return b.toString();
}
+ protected int getSchemaVersion() {
+ return getSchema().getVersion();
+ }
+
+ protected Schema<ProjectData> getSchema() {
+ return indexes.getSearchIndex().getSchema();
+ }
+
protected static Iterable<String> names(ProjectInfo... projects) {
return names(Arrays.asList(projects));
}
diff --git a/javatests/com/google/gerrit/server/query/project/BUILD b/javatests/com/google/gerrit/server/query/project/BUILD
index eaa3df3..f0c455e 100644
--- a/javatests/com/google/gerrit/server/query/project/BUILD
+++ b/javatests/com/google/gerrit/server/query/project/BUILD
@@ -9,6 +9,8 @@
visibility = ["//visibility:public"],
deps = [
"//java/com/google/gerrit/extensions:api",
+ "//java/com/google/gerrit/index",
+ "//java/com/google/gerrit/index/project",
"//java/com/google/gerrit/lifecycle",
"//java/com/google/gerrit/reviewdb:server",
"//java/com/google/gerrit/server",