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",