Merge branch 'stable-2.16'

* stable-2.16:
  ListChildProjects: Make settting recursive option chainable
  ListChildProjects: Add limit option
  ListChildProjects: Use parent predicate to find child projects
  QueryProjects: Make setter methods chainable
  Project index: Add parent predicate

ListChildProjectsIT#listChildrenWithLimit is adjusted to use the
ProjectOperations interface.

Change-Id: I185c204f6acbf542f7d80454fe67ce892147473a
This commit is contained in:
David Pursehouse
2019-02-05 10:16:20 +09:00
13 changed files with 133 additions and 56 deletions

View File

@@ -12,6 +12,11 @@ name:'NAME'::
+
Matches projects that have exactly the name 'NAME'.
[[parent]]
parent:'PARENT'::
+
Matches projects that have 'PARENT' as parent project.
[[inname]]
inname:'NAME'::
+

View File

@@ -106,6 +106,8 @@ public interface ProjectApi {
List<ProjectInfo> children(boolean recursive) throws RestApiException;
List<ProjectInfo> children(int limit) throws RestApiException;
ChildProjectApi child(String name) throws RestApiException;
/**
@@ -284,6 +286,11 @@ public interface ProjectApi {
throw new NotImplementedException();
}
@Override
public List<ProjectInfo> children(int limit) throws RestApiException {
throw new NotImplementedException();
}
@Override
public ChildProjectApi child(String name) throws RestApiException {
throw new NotImplementedException();

View File

@@ -70,7 +70,6 @@ import com.google.gerrit.server.restapi.project.GetHead;
import com.google.gerrit.server.restapi.project.GetParent;
import com.google.gerrit.server.restapi.project.Index;
import com.google.gerrit.server.restapi.project.ListBranches;
import com.google.gerrit.server.restapi.project.ListChildProjects;
import com.google.gerrit.server.restapi.project.ListDashboards;
import com.google.gerrit.server.restapi.project.ListTags;
import com.google.gerrit.server.restapi.project.ProjectsCollection;
@@ -475,10 +474,17 @@ public class ProjectApiImpl implements ProjectApi {
@Override
public List<ProjectInfo> children(boolean recursive) throws RestApiException {
ListChildProjects list = children.list();
list.setRecursive(recursive);
try {
return list.apply(checkExists());
return children.list().withRecursive(recursive).apply(checkExists());
} catch (Exception e) {
throw asRestApiException("Cannot list children", e);
}
}
@Override
public List<ProjectInfo> children(int limit) throws RestApiException {
try {
return children.list().withLimit(limit).apply(checkExists());
} catch (Exception e) {
throw asRestApiException("Cannot list children", e);
}

View File

@@ -150,12 +150,12 @@ class ProjectsImpl implements Projects {
private List<ProjectInfo> query(QueryRequest r) throws RestApiException {
try {
QueryProjects myQueryProjects = queryProvider.get();
myQueryProjects.setQuery(r.getQuery());
myQueryProjects.setLimit(r.getLimit());
myQueryProjects.setStart(r.getStart());
return myQueryProjects.apply(TopLevelResource.INSTANCE);
return queryProvider
.get()
.withQuery(r.getQuery())
.withLimit(r.getLimit())
.withStart(r.getStart())
.apply(TopLevelResource.INSTANCE);
} catch (OrmException e) {
throw new RestApiException("Cannot query projects", e);
}

View File

@@ -27,6 +27,10 @@ public class ProjectPredicates {
return new ProjectPredicate(ProjectField.NAME, nameKey.get());
}
public static Predicate<ProjectData> parent(Project.NameKey parentNameKey) {
return new ProjectPredicate(ProjectField.PARENT_NAME, parentNameKey.get());
}
public static Predicate<ProjectData> inname(String name) {
return new ProjectPredicate(ProjectField.NAME_PART, name.toLowerCase(Locale.US));
}

View File

@@ -44,6 +44,11 @@ public class ProjectQueryBuilder extends QueryBuilder<ProjectData> {
return ProjectPredicates.name(new Project.NameKey(name));
}
@Operator
public Predicate<ProjectData> parent(String parentName) {
return ProjectPredicates.parent(new Project.NameKey(parentName));
}
@Operator
public Predicate<ProjectData> inname(String namePart) {
if (namePart.isEmpty()) {

View File

@@ -22,9 +22,8 @@ import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.extensions.annotations.RequiresCapability;
import com.google.gerrit.extensions.api.projects.IndexProjectInput;
import com.google.gerrit.extensions.common.ProjectInfo;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.index.project.ProjectIndexer;
import com.google.gerrit.reviewdb.client.Project;
@@ -59,15 +58,12 @@ public class Index implements RestModifyView<ProjectResource, IndexProjectInput>
@Override
public Response.Accepted apply(ProjectResource rsrc, IndexProjectInput input)
throws IOException, AuthException, OrmException, PermissionBackendException,
ResourceConflictException {
throws IOException, OrmException, PermissionBackendException, RestApiException {
String response = "Project " + rsrc.getName() + " submitted for reindexing";
reindex(rsrc.getNameKey(), input.async);
if (Boolean.TRUE.equals(input.indexChildren)) {
ListChildProjects listChildProjects = listChildProjectsProvider.get();
listChildProjects.setRecursive(true);
for (ProjectInfo child : listChildProjects.apply(rsrc)) {
for (ProjectInfo child : listChildProjectsProvider.get().withRecursive(true).apply(rsrc)) {
reindex(new Project.NameKey(child.name), input.async);
}

View File

@@ -17,22 +17,21 @@ package com.google.gerrit.server.restapi.project;
import static java.util.stream.Collectors.toList;
import com.google.gerrit.extensions.common.ProjectInfo;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.extensions.restapi.TopLevelResource;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.permissions.ProjectPermission;
import com.google.gerrit.server.project.ChildProjects;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectJson;
import com.google.gerrit.server.project.ProjectResource;
import com.google.gerrit.server.project.ProjectState;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import java.util.HashMap;
import com.google.inject.Provider;
import java.util.List;
import java.util.Map;
import org.kohsuke.args4j.Option;
public class ListChildProjects implements RestReadView<ProjectResource> {
@@ -40,33 +39,42 @@ public class ListChildProjects implements RestReadView<ProjectResource> {
@Option(name = "--recursive", usage = "to list child projects recursively")
private boolean recursive;
private final ProjectCache projectCache;
@Option(name = "--limit", usage = "maximum number of parents projects to list")
private int limit;
private final PermissionBackend permissionBackend;
private final AllProjectsName allProjects;
private final ProjectJson json;
private final ChildProjects childProjects;
private final Provider<QueryProjects> queryProvider;
@Inject
ListChildProjects(
ProjectCache projectCache,
PermissionBackend permissionBackend,
AllProjectsName allProjectsName,
ProjectJson json,
ChildProjects childProjects) {
this.projectCache = projectCache;
ChildProjects childProjects,
Provider<QueryProjects> queryProvider) {
this.permissionBackend = permissionBackend;
this.allProjects = allProjectsName;
this.json = json;
this.childProjects = childProjects;
this.queryProvider = queryProvider;
}
public void setRecursive(boolean recursive) {
public ListChildProjects withRecursive(boolean recursive) {
this.recursive = recursive;
return this;
}
public ListChildProjects withLimit(int limit) {
this.limit = limit;
return this;
}
@Override
public List<ProjectInfo> apply(ProjectResource rsrc)
throws PermissionBackendException, ResourceConflictException {
throws PermissionBackendException, OrmException, RestApiException {
if (limit < 0) {
throw new BadRequestException("limit must be a positive number");
}
if (recursive && limit != 0) {
throw new ResourceConflictException("recursive and limit options are mutually exclusive");
}
rsrc.getProjectState().checkStatePermitsRead();
if (recursive) {
return childProjects.list(rsrc.getNameKey());
@@ -76,22 +84,19 @@ public class ListChildProjects implements RestReadView<ProjectResource> {
}
private List<ProjectInfo> directChildProjects(Project.NameKey parent)
throws PermissionBackendException {
Map<Project.NameKey, Project> children = new HashMap<>();
for (Project.NameKey name : projectCache.all()) {
ProjectState c = projectCache.get(name);
if (c != null
&& parent.equals(c.getProject().getParent(allProjects))
&& c.statePermitsRead()) {
children.put(c.getNameKey(), c.getProject());
}
}
return permissionBackend
.currentUser()
.filter(ProjectPermission.ACCESS, children.keySet())
throws OrmException, RestApiException {
PermissionBackend.WithUser currentUser = permissionBackend.currentUser();
return queryProvider
.get()
.withQuery("parent:" + parent.get())
.withLimit(limit)
.apply(TopLevelResource.INSTANCE)
.stream()
.sorted()
.map((p) -> json.format(children.get(p)))
.filter(
p ->
currentUser
.project(new Project.NameKey(p.name))
.testOrFalse(ProjectPermission.ACCESS))
.collect(toList());
}
}

View File

@@ -49,8 +49,9 @@ public class QueryProjects implements RestReadView<TopLevelResource> {
name = "--query",
aliases = {"-q"},
usage = "project query")
public void setQuery(String query) {
public QueryProjects withQuery(String query) {
this.query = query;
return this;
}
@Option(
@@ -58,8 +59,9 @@ public class QueryProjects implements RestReadView<TopLevelResource> {
aliases = {"-n"},
metaVar = "CNT",
usage = "maximum number of projects to list")
public void setLimit(int limit) {
public QueryProjects withLimit(int limit) {
this.limit = limit;
return this;
}
@Option(
@@ -67,8 +69,9 @@ public class QueryProjects implements RestReadView<TopLevelResource> {
aliases = {"-S"},
metaVar = "CNT",
usage = "number of projects to skip")
public void setStart(int start) {
public QueryProjects withStart(int start) {
this.start = start;
return this;
}
@Inject

View File

@@ -33,6 +33,7 @@ import com.google.gerrit.server.restapi.project.ListChildProjects;
import com.google.gerrit.server.restapi.project.SetParent;
import com.google.gerrit.sshd.CommandMetaData;
import com.google.gerrit.sshd.SshCommand;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import java.io.IOException;
import java.util.ArrayList;
@@ -111,7 +112,7 @@ final class SetParentCommand extends SshCommand {
childProjects.addAll(getChildrenForReparenting(oldParent));
} catch (PermissionBackendException e) {
throw new Failure(1, "permissions unavailable", e);
} catch (RestApiException e) {
} catch (OrmException | RestApiException e) {
throw new Failure(1, "failure in request", e);
}
}
@@ -148,7 +149,7 @@ final class SetParentCommand extends SshCommand {
* reparenting.
*/
private List<Project.NameKey> getChildrenForReparenting(ProjectState parent)
throws PermissionBackendException, RestApiException {
throws PermissionBackendException, OrmException, RestApiException {
final List<Project.NameKey> childProjects = new ArrayList<>();
final List<Project.NameKey> excluded = new ArrayList<>(excludedChildren.size());
for (ProjectState excludedChild : excludedChildren) {

View File

@@ -52,6 +52,17 @@ public class ListChildProjectsIT extends AbstractDaemonTest {
.containsExactly(child1_1, child1_2);
}
@Test
public void listChildrenWithLimit() throws Exception {
String prefix = RandomStringUtils.randomAlphabetic(8);
Project.NameKey child1 = projectOperations.newProject().name(prefix + "p1").create();
Project.NameKey child1_1 =
projectOperations.newProject().parent(child1).name(prefix + "p1.1").create();
projectOperations.newProject().parent(child1).name(prefix + "p1.2").create();
assertThatNameList(gApi.projects().name(child1.get()).children(1)).containsExactly(child1_1);
}
@Test
public void listChildrenRecursively() throws Exception {
String prefix = RandomStringUtils.randomAlphabetic(8);

View File

@@ -20,6 +20,7 @@ import static java.util.stream.Collectors.toList;
import com.google.common.base.CharMatcher;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.gerrit.extensions.api.GerritApi;
import com.google.gerrit.extensions.api.access.AccessSectionInfo;
import com.google.gerrit.extensions.api.access.PermissionInfo;
@@ -48,6 +49,7 @@ import com.google.gerrit.server.account.Accounts;
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.config.AllUsersName;
import com.google.gerrit.server.group.SystemGroupBackend;
import com.google.gerrit.server.schema.SchemaCreator;
import com.google.gerrit.server.util.ManualRequestContext;
@@ -62,6 +64,7 @@ import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
@@ -93,6 +96,8 @@ public abstract class AbstractQueryProjectsTest extends GerritServerTests {
@Inject protected AllProjectsName allProjects;
@Inject protected AllUsersName allUsers;
protected LifecycleManager lifecycle;
protected Injector injector;
protected AccountInfo currentUserInfo;
@@ -159,6 +164,28 @@ public abstract class AbstractQueryProjectsTest extends GerritServerTests {
assertQuery("name:" + projectWithHyphen.name, projectWithHyphen);
}
@Test
public void byParent() throws Exception {
assertQuery("parent:project");
ProjectInfo parent = createProject(name("parent"));
assertQuery("parent:" + parent.name);
ProjectInfo child = createProject(name("child"), parent.name);
assertQuery("parent:" + parent.name, child);
}
@Test
public void byParentOfAllProjects() throws Exception {
Set<String> excludedProjects = ImmutableSet.of(allProjects.get(), allUsers.get());
ProjectInfo[] projects =
gApi.projects()
.list()
.get()
.stream()
.filter(p -> !excludedProjects.contains(p.name))
.toArray(s -> new ProjectInfo[s]);
assertQuery("parent:" + allProjects.get(), projects);
}
@Test
public void byInname() throws Exception {
String namePart = getSanitizedMethodName();
@@ -296,6 +323,13 @@ public abstract class AbstractQueryProjectsTest extends GerritServerTests {
return gApi.projects().create(in).get();
}
protected ProjectInfo createProject(String name, String parent) throws Exception {
ProjectInput in = new ProjectInput();
in.name = name;
in.parent = parent;
return gApi.projects().create(in).get();
}
protected ProjectInfo createProjectWithDescription(String name, String description)
throws Exception {
ProjectInput in = new ProjectInput();