summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorShawn Pearce <sop@google.com>2013-10-18 01:48:07 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2013-10-18 01:48:07 +0000
commit8f9fb52184ed55b3390d36c0408756c597091e1c (patch)
tree3e59bf82d5c73e08de2a8d0de86ad72723ea8971
parent3e019c2a656e2501d9b7492847b8c626f2c5450c (diff)
parent42f7bc332360dac468adb5387c475d71b3ec42a8 (diff)
Merge changes I9caec692,I38af4c45 into stable-2.8
* changes: Check preconditions when setting parent project through REST Add acceptance tests for setting parent project via REST
-rw-r--r--gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/SetParentIT.java155
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/project/SetParent.java43
2 files changed, 191 insertions, 7 deletions
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/SetParentIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/SetParentIT.java
new file mode 100644
index 0000000000..abc3bb69a7
--- /dev/null
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/SetParentIT.java
@@ -0,0 +1,155 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.acceptance.rest.project;
+
+import static com.google.gerrit.acceptance.git.GitUtil.createProject;
+import static com.google.gerrit.acceptance.git.GitUtil.initSsh;
+import static org.junit.Assert.assertEquals;
+
+import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.AccountCreator;
+import com.google.gerrit.acceptance.RestResponse;
+import com.google.gerrit.acceptance.RestSession;
+import com.google.gerrit.acceptance.SshSession;
+import com.google.gerrit.acceptance.TestAccount;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.server.config.AllProjectsNameProvider;
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
+import com.google.inject.Inject;
+
+import com.jcraft.jsch.JSchException;
+
+import org.apache.http.HttpStatus;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.IOException;
+
+public class SetParentIT extends AbstractDaemonTest {
+
+ @Inject
+ private AccountCreator accounts;
+
+ @Inject
+ private AllProjectsNameProvider allProjects;
+
+ private RestSession adminSession;
+ private RestSession userSession;
+ private SshSession sshSession;
+
+ private String project;
+
+ @Before
+ public void setUp() throws Exception {
+ TestAccount admin = accounts.admin();
+ adminSession = new RestSession(server, admin);
+
+ TestAccount user = accounts.create("user", "user@example.com", "User");
+ userSession = new RestSession(server, user);
+
+
+ initSsh(admin);
+ sshSession = new SshSession(server, admin);
+ project = "p";
+ createProject(sshSession, project, null, true);
+ }
+
+ @After
+ public void cleanup() {
+ sshSession.close();
+ }
+
+ @Test
+ public void setParent_Forbidden() throws IOException, JSchException {
+ String parent = "parent";
+ createProject(sshSession, parent, null, true);
+ RestResponse r =
+ userSession.put("/projects/" + project + "/parent",
+ new ParentInput(parent));
+ assertEquals(HttpStatus.SC_FORBIDDEN, r.getStatusCode());
+ r.consume();
+ }
+
+ @Test
+ public void setParent() throws IOException, JSchException {
+ String parent = "parent";
+ createProject(sshSession, parent, null, true);
+ RestResponse r =
+ adminSession.put("/projects/" + project + "/parent",
+ new ParentInput(parent));
+ assertEquals(HttpStatus.SC_OK, r.getStatusCode());
+ r.consume();
+
+ r = adminSession.get("/projects/" + project + "/parent");
+ assertEquals(HttpStatus.SC_OK, r.getStatusCode());
+ String newParent =
+ (new Gson()).fromJson(r.getReader(),
+ new TypeToken<String>() {}.getType());
+ assertEquals(parent, newParent);
+ r.consume();
+ }
+
+ @Test
+ public void setParentForAllProjects_Conflict() throws IOException {
+ RestResponse r =
+ adminSession.put("/projects/" + allProjects.get() + "/parent",
+ new ParentInput(project));
+ assertEquals(HttpStatus.SC_CONFLICT, r.getStatusCode());
+ r.consume();
+ }
+
+ @Test
+ public void setInvalidParent_Conflict() throws IOException, JSchException {
+ RestResponse r =
+ adminSession.put("/projects/" + project + "/parent",
+ new ParentInput(project));
+ assertEquals(HttpStatus.SC_CONFLICT, r.getStatusCode());
+ r.consume();
+
+ String child = "child";
+ createProject(sshSession, child, new Project.NameKey(project), true);
+ r = adminSession.put("/projects/" + project + "/parent",
+ new ParentInput(child));
+ assertEquals(HttpStatus.SC_CONFLICT, r.getStatusCode());
+ r.consume();
+
+ String grandchild = "grandchild";
+ createProject(sshSession, grandchild, new Project.NameKey(child), true);
+ r = adminSession.put("/projects/" + project + "/parent",
+ new ParentInput(grandchild));
+ assertEquals(HttpStatus.SC_CONFLICT, r.getStatusCode());
+ r.consume();
+ }
+
+ @Test
+ public void setNonExistingParent_UnprocessibleEntity() throws IOException {
+ RestResponse r =
+ adminSession.put("/projects/" + project + "/parent",
+ new ParentInput("non-existing"));
+ assertEquals(HttpStatus.SC_UNPROCESSABLE_ENTITY, r.getStatusCode());
+ r.consume();
+ }
+
+ @SuppressWarnings("unused")
+ private static class ParentInput {
+ String parent;
+
+ ParentInput(String parent) {
+ this.parent = parent;
+ }
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/SetParent.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/SetParent.java
index c1fb5de46e..999358cdbe 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/SetParent.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/SetParent.java
@@ -15,13 +15,16 @@
package com.google.gerrit.server.project;
import com.google.common.base.Objects;
+import com.google.common.base.Predicate;
import com.google.common.base.Strings;
+import com.google.common.collect.Iterables;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.DefaultInput;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.RestModifyView;
+import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.config.AllProjectsName;
@@ -33,6 +36,8 @@ import com.google.inject.Inject;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
+import java.io.IOException;
+
class SetParent implements RestModifyView<ProjectResource, Input> {
static class Input {
@DefaultInput
@@ -54,21 +59,45 @@ class SetParent implements RestModifyView<ProjectResource, Input> {
}
@Override
- public String apply(ProjectResource resource, Input input)
- throws AuthException, BadRequestException, ResourceConflictException,
- Exception {
- ProjectControl ctl = resource.getControl();
+ public String apply(final ProjectResource rsrc, Input input) throws AuthException,
+ BadRequestException, ResourceConflictException,
+ ResourceNotFoundException, UnprocessableEntityException, IOException {
+ ProjectControl ctl = rsrc.getControl();
IdentifiedUser user = (IdentifiedUser) ctl.getCurrentUser();
if (!user.getCapabilities().canAdministrateServer()) {
throw new AuthException("not administrator");
}
+ if (rsrc.getNameKey().equals(allProjects)) {
+ throw new ResourceConflictException("cannot set parent of "
+ + allProjects.get());
+ }
+
+ input.parent = Strings.emptyToNull(input.parent);
+ if (input.parent != null) {
+ ProjectState parent = cache.get(new Project.NameKey(input.parent));
+ if (parent == null) {
+ throw new UnprocessableEntityException("parent project " + input.parent
+ + " not found");
+ }
+
+ if (Iterables.tryFind(parent.tree(), new Predicate<ProjectState>() {
+ @Override
+ public boolean apply(ProjectState input) {
+ return input.getProject().getNameKey().equals(rsrc.getNameKey());
+ }
+ }).isPresent()) {
+ throw new ResourceConflictException("cycle exists between "
+ + rsrc.getName() + " and " + parent.getProject().getName());
+ }
+ }
+
try {
- MetaDataUpdate md = updateFactory.create(resource.getNameKey());
+ MetaDataUpdate md = updateFactory.create(rsrc.getNameKey());
try {
ProjectConfig config = ProjectConfig.read(md);
Project project = config.getProject();
- project.setParentName(Strings.emptyToNull(input.parent));
+ project.setParentName(input.parent);
String msg = Strings.emptyToNull(input.commitMessage);
if (msg == null) {
@@ -89,7 +118,7 @@ class SetParent implements RestModifyView<ProjectResource, Input> {
md.close();
}
} catch (RepositoryNotFoundException notFound) {
- throw new ResourceNotFoundException(resource.getName());
+ throw new ResourceNotFoundException(rsrc.getName());
} catch (ConfigInvalidException e) {
throw new ResourceConflictException(String.format(
"invalid project.config: %s", e.getMessage()));