Merge changes from topic 'delete-branches-api'
* changes: ProjectApi: Add method to delete branches DeleteBranches: Split static Input class out to extension API
This commit is contained in:
		@@ -0,0 +1,153 @@
 | 
			
		||||
// Copyright (C) 2016 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.common.truth.Truth.assertThat;
 | 
			
		||||
import static com.google.gerrit.acceptance.rest.project.BranchAssert.assertRefNames;
 | 
			
		||||
import static org.junit.Assert.fail;
 | 
			
		||||
 | 
			
		||||
import com.google.common.collect.ImmutableList;
 | 
			
		||||
import com.google.common.collect.Lists;
 | 
			
		||||
import com.google.gerrit.acceptance.AbstractDaemonTest;
 | 
			
		||||
import com.google.gerrit.acceptance.NoHttpd;
 | 
			
		||||
import com.google.gerrit.extensions.api.projects.BranchInput;
 | 
			
		||||
import com.google.gerrit.extensions.api.projects.DeleteBranchesInput;
 | 
			
		||||
import com.google.gerrit.extensions.api.projects.ProjectApi;
 | 
			
		||||
import com.google.gerrit.extensions.restapi.ResourceConflictException;
 | 
			
		||||
 | 
			
		||||
import org.eclipse.jgit.revwalk.RevCommit;
 | 
			
		||||
import org.junit.Before;
 | 
			
		||||
import org.junit.Test;
 | 
			
		||||
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
@NoHttpd
 | 
			
		||||
public class DeleteBranchesIT extends AbstractDaemonTest {
 | 
			
		||||
  private static final List<String> BRANCHES = ImmutableList.of(
 | 
			
		||||
      "refs/heads/test-1", "refs/heads/test-2", "refs/heads/test-3");
 | 
			
		||||
 | 
			
		||||
  @Before
 | 
			
		||||
  public void setUp() throws Exception {
 | 
			
		||||
    for (String name : BRANCHES) {
 | 
			
		||||
      project().branch(name).create(new BranchInput());
 | 
			
		||||
    }
 | 
			
		||||
    assertBranches(BRANCHES);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Test
 | 
			
		||||
  public void deleteBranches() throws Exception {
 | 
			
		||||
    HashMap<String, RevCommit> initialRevisions = initialRevisions(BRANCHES);
 | 
			
		||||
    DeleteBranchesInput input = new DeleteBranchesInput();
 | 
			
		||||
    input.branches = BRANCHES;
 | 
			
		||||
    project().deleteBranches(input);
 | 
			
		||||
    assertBranchesDeleted();
 | 
			
		||||
    assertRefUpdatedEvents(initialRevisions);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Test
 | 
			
		||||
  public void deleteBranchesForbidden() throws Exception {
 | 
			
		||||
    DeleteBranchesInput input = new DeleteBranchesInput();
 | 
			
		||||
    input.branches = BRANCHES;
 | 
			
		||||
    setApiUser(user);
 | 
			
		||||
    try {
 | 
			
		||||
      project().deleteBranches(input);
 | 
			
		||||
      fail("Expected ResourceConflictException");
 | 
			
		||||
    } catch (ResourceConflictException e) {
 | 
			
		||||
      assertThat(e).hasMessage(errorMessageForBranches(BRANCHES));
 | 
			
		||||
    }
 | 
			
		||||
    setApiUser(admin);
 | 
			
		||||
    assertBranches(BRANCHES);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Test
 | 
			
		||||
  public void deleteBranchesNotFound() throws Exception {
 | 
			
		||||
    DeleteBranchesInput input = new DeleteBranchesInput();
 | 
			
		||||
    List<String> branches = Lists.newArrayList(BRANCHES);
 | 
			
		||||
    branches.add("refs/heads/does-not-exist");
 | 
			
		||||
    input.branches = branches;
 | 
			
		||||
    try {
 | 
			
		||||
      project().deleteBranches(input);
 | 
			
		||||
      fail("Expected ResourceConflictException");
 | 
			
		||||
    } catch (ResourceConflictException e) {
 | 
			
		||||
      assertThat(e).hasMessage(errorMessageForBranches(
 | 
			
		||||
          ImmutableList.of("refs/heads/does-not-exist")));
 | 
			
		||||
    }
 | 
			
		||||
    assertBranchesDeleted();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Test
 | 
			
		||||
  public void deleteBranchesNotFoundContinue() throws Exception {
 | 
			
		||||
    // If it fails on the first branch in the input, it should still
 | 
			
		||||
    // continue to process the remaining branches.
 | 
			
		||||
    DeleteBranchesInput input = new DeleteBranchesInput();
 | 
			
		||||
    List<String> branches = Lists.newArrayList("refs/heads/does-not-exist");
 | 
			
		||||
    branches.addAll(BRANCHES);
 | 
			
		||||
    input.branches = branches;
 | 
			
		||||
    try {
 | 
			
		||||
      project().deleteBranches(input);
 | 
			
		||||
      fail("Expected ResourceConflictException");
 | 
			
		||||
    } catch (ResourceConflictException e) {
 | 
			
		||||
      assertThat(e).hasMessage(errorMessageForBranches(
 | 
			
		||||
          ImmutableList.of("refs/heads/does-not-exist")));
 | 
			
		||||
    }
 | 
			
		||||
    assertBranchesDeleted();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private String errorMessageForBranches(List<String> branches) {
 | 
			
		||||
    StringBuilder message = new StringBuilder();
 | 
			
		||||
    for (String branch : branches) {
 | 
			
		||||
      message.append("Cannot delete ")
 | 
			
		||||
        .append(branch)
 | 
			
		||||
        .append(": it doesn't exist or you do not have permission ")
 | 
			
		||||
        .append("to delete it\n");
 | 
			
		||||
    }
 | 
			
		||||
    return message.toString();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private HashMap<String, RevCommit> initialRevisions(List<String> branches)
 | 
			
		||||
      throws Exception {
 | 
			
		||||
    HashMap<String, RevCommit> result = new HashMap<>();
 | 
			
		||||
    for (String branch : branches) {
 | 
			
		||||
      result.put(branch, getRemoteHead(project, branch));
 | 
			
		||||
    }
 | 
			
		||||
    return result;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private void assertRefUpdatedEvents(HashMap<String, RevCommit> revisions)
 | 
			
		||||
      throws Exception {
 | 
			
		||||
    for (String branch : revisions.keySet()) {
 | 
			
		||||
      RevCommit revision = revisions.get(branch);
 | 
			
		||||
      eventRecorder.assertRefUpdatedEvents(project.get(), branch,
 | 
			
		||||
          null, revision,
 | 
			
		||||
          revision, null);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private ProjectApi project() throws Exception {
 | 
			
		||||
    return gApi.projects().name(project.get());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private void assertBranches(List<String> branches) throws Exception {
 | 
			
		||||
    List<String> expected = Lists.newArrayList(
 | 
			
		||||
        "HEAD", "refs/meta/config", "refs/heads/master");
 | 
			
		||||
    expected.addAll(branches);
 | 
			
		||||
    assertRefNames(expected, project().branches().get());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private void assertBranchesDeleted() throws Exception {
 | 
			
		||||
    assertBranches(ImmutableList.<String>of());
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,21 @@
 | 
			
		||||
// Copyright (C) 2016 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.extensions.api.projects;
 | 
			
		||||
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
public class DeleteBranchesInput {
 | 
			
		||||
  public List<String> branches;
 | 
			
		||||
}
 | 
			
		||||
@@ -39,6 +39,8 @@ public interface ProjectApi {
 | 
			
		||||
  ListRefsRequest<BranchInfo> branches();
 | 
			
		||||
  ListRefsRequest<TagInfo> tags();
 | 
			
		||||
 | 
			
		||||
  void deleteBranches(DeleteBranchesInput in) throws RestApiException;
 | 
			
		||||
 | 
			
		||||
  abstract class ListRefsRequest<T extends RefInfo> {
 | 
			
		||||
    protected int limit;
 | 
			
		||||
    protected int start;
 | 
			
		||||
@@ -198,5 +200,10 @@ public interface ProjectApi {
 | 
			
		||||
    public TagApi tag(String ref) throws RestApiException {
 | 
			
		||||
      throw new NotImplementedException();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void deleteBranches(DeleteBranchesInput in) throws RestApiException {
 | 
			
		||||
      throw new NotImplementedException();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -23,6 +23,7 @@ import com.google.gerrit.extensions.api.projects.BranchInfo;
 | 
			
		||||
import com.google.gerrit.extensions.api.projects.ChildProjectApi;
 | 
			
		||||
import com.google.gerrit.extensions.api.projects.ConfigInfo;
 | 
			
		||||
import com.google.gerrit.extensions.api.projects.ConfigInput;
 | 
			
		||||
import com.google.gerrit.extensions.api.projects.DeleteBranchesInput;
 | 
			
		||||
import com.google.gerrit.extensions.api.projects.DescriptionInput;
 | 
			
		||||
import com.google.gerrit.extensions.api.projects.ProjectApi;
 | 
			
		||||
import com.google.gerrit.extensions.api.projects.ProjectInput;
 | 
			
		||||
@@ -38,6 +39,7 @@ import com.google.gerrit.extensions.restapi.TopLevelResource;
 | 
			
		||||
import com.google.gerrit.server.CurrentUser;
 | 
			
		||||
import com.google.gerrit.server.project.ChildProjectsCollection;
 | 
			
		||||
import com.google.gerrit.server.project.CreateProject;
 | 
			
		||||
import com.google.gerrit.server.project.DeleteBranches;
 | 
			
		||||
import com.google.gerrit.server.project.GetAccess;
 | 
			
		||||
import com.google.gerrit.server.project.GetConfig;
 | 
			
		||||
import com.google.gerrit.server.project.GetDescription;
 | 
			
		||||
@@ -50,6 +52,7 @@ import com.google.gerrit.server.project.ProjectsCollection;
 | 
			
		||||
import com.google.gerrit.server.project.PutConfig;
 | 
			
		||||
import com.google.gerrit.server.project.PutDescription;
 | 
			
		||||
import com.google.gerrit.server.project.SetAccess;
 | 
			
		||||
import com.google.gwtorm.server.OrmException;
 | 
			
		||||
import com.google.inject.assistedinject.Assisted;
 | 
			
		||||
import com.google.inject.assistedinject.AssistedInject;
 | 
			
		||||
 | 
			
		||||
@@ -83,6 +86,7 @@ public class ProjectApiImpl implements ProjectApi {
 | 
			
		||||
  private final PutConfig putConfig;
 | 
			
		||||
  private final ListBranches listBranches;
 | 
			
		||||
  private final ListTags listTags;
 | 
			
		||||
  private final DeleteBranches deleteBranches;
 | 
			
		||||
 | 
			
		||||
  @AssistedInject
 | 
			
		||||
  ProjectApiImpl(CurrentUser user,
 | 
			
		||||
@@ -102,11 +106,12 @@ public class ProjectApiImpl implements ProjectApi {
 | 
			
		||||
      PutConfig putConfig,
 | 
			
		||||
      ListBranches listBranches,
 | 
			
		||||
      ListTags listTags,
 | 
			
		||||
      DeleteBranches deleteBranches,
 | 
			
		||||
      @Assisted ProjectResource project) {
 | 
			
		||||
    this(user, createProjectFactory, projectApi, projects, getDescription,
 | 
			
		||||
        putDescription, childApi, children, projectJson, branchApiFactory,
 | 
			
		||||
        tagApiFactory, getAccess, setAccess, getConfig, putConfig, listBranches,
 | 
			
		||||
        listTags, project, null);
 | 
			
		||||
        listTags, deleteBranches, project, null);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @AssistedInject
 | 
			
		||||
@@ -127,11 +132,12 @@ public class ProjectApiImpl implements ProjectApi {
 | 
			
		||||
      PutConfig putConfig,
 | 
			
		||||
      ListBranches listBranches,
 | 
			
		||||
      ListTags listTags,
 | 
			
		||||
      DeleteBranches deleteBranches,
 | 
			
		||||
      @Assisted String name) {
 | 
			
		||||
    this(user, createProjectFactory, projectApi, projects, getDescription,
 | 
			
		||||
        putDescription, childApi, children, projectJson, branchApiFactory,
 | 
			
		||||
        tagApiFactory, getAccess, setAccess, getConfig, putConfig, listBranches,
 | 
			
		||||
        listTags, null, name);
 | 
			
		||||
        listTags, deleteBranches, null, name);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private ProjectApiImpl(CurrentUser user,
 | 
			
		||||
@@ -151,6 +157,7 @@ public class ProjectApiImpl implements ProjectApi {
 | 
			
		||||
      PutConfig putConfig,
 | 
			
		||||
      ListBranches listBranches,
 | 
			
		||||
      ListTags listTags,
 | 
			
		||||
      DeleteBranches deleteBranches,
 | 
			
		||||
      ProjectResource project,
 | 
			
		||||
      String name) {
 | 
			
		||||
    this.user = user;
 | 
			
		||||
@@ -172,6 +179,7 @@ public class ProjectApiImpl implements ProjectApi {
 | 
			
		||||
    this.putConfig = putConfig;
 | 
			
		||||
    this.listBranches = listBranches;
 | 
			
		||||
    this.listTags = listTags;
 | 
			
		||||
    this.deleteBranches = deleteBranches;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Override
 | 
			
		||||
@@ -328,6 +336,15 @@ public class ProjectApiImpl implements ProjectApi {
 | 
			
		||||
    return tagApi.create(checkExists(), ref);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Override
 | 
			
		||||
  public void deleteBranches(DeleteBranchesInput in) throws RestApiException {
 | 
			
		||||
    try {
 | 
			
		||||
      deleteBranches.apply(checkExists(), in);
 | 
			
		||||
    } catch (OrmException | IOException e) {
 | 
			
		||||
      throw new RestApiException("Cannot delete branches", e);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private ProjectResource checkExists() throws ResourceNotFoundException {
 | 
			
		||||
    if (project == null) {
 | 
			
		||||
      throw new ResourceNotFoundException(name);
 | 
			
		||||
 
 | 
			
		||||
@@ -18,6 +18,7 @@ import static java.lang.String.format;
 | 
			
		||||
 | 
			
		||||
import com.google.common.collect.Lists;
 | 
			
		||||
import com.google.gerrit.common.ChangeHooks;
 | 
			
		||||
import com.google.gerrit.extensions.api.projects.DeleteBranchesInput;
 | 
			
		||||
import com.google.gerrit.extensions.restapi.ResourceConflictException;
 | 
			
		||||
import com.google.gerrit.extensions.restapi.Response;
 | 
			
		||||
import com.google.gerrit.extensions.restapi.RestModifyView;
 | 
			
		||||
@@ -25,7 +26,6 @@ import com.google.gerrit.reviewdb.client.Branch;
 | 
			
		||||
import com.google.gerrit.server.IdentifiedUser;
 | 
			
		||||
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
 | 
			
		||||
import com.google.gerrit.server.git.GitRepositoryManager;
 | 
			
		||||
import com.google.gerrit.server.project.DeleteBranches.Input;
 | 
			
		||||
import com.google.gerrit.server.query.change.InternalChangeQuery;
 | 
			
		||||
import com.google.gwtorm.server.OrmException;
 | 
			
		||||
import com.google.inject.Inject;
 | 
			
		||||
@@ -44,26 +44,12 @@ import org.slf4j.Logger;
 | 
			
		||||
import org.slf4j.LoggerFactory;
 | 
			
		||||
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
@Singleton
 | 
			
		||||
class DeleteBranches implements RestModifyView<ProjectResource, Input> {
 | 
			
		||||
public class DeleteBranches
 | 
			
		||||
    implements RestModifyView<ProjectResource, DeleteBranchesInput> {
 | 
			
		||||
  private static final Logger log = LoggerFactory.getLogger(DeleteBranches.class);
 | 
			
		||||
 | 
			
		||||
  static class Input {
 | 
			
		||||
    List<String> branches;
 | 
			
		||||
 | 
			
		||||
    static Input init(Input in) {
 | 
			
		||||
      if (in == null) {
 | 
			
		||||
        in = new Input();
 | 
			
		||||
      }
 | 
			
		||||
      if (in.branches == null) {
 | 
			
		||||
        in.branches = Lists.newArrayListWithCapacity(1);
 | 
			
		||||
      }
 | 
			
		||||
      return in;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private final Provider<IdentifiedUser> identifiedUser;
 | 
			
		||||
  private final GitRepositoryManager repoManager;
 | 
			
		||||
  private final Provider<InternalChangeQuery> queryProvider;
 | 
			
		||||
@@ -84,9 +70,16 @@ class DeleteBranches implements RestModifyView<ProjectResource, Input> {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Override
 | 
			
		||||
  public Response<?> apply(ProjectResource project, Input input)
 | 
			
		||||
  public Response<?> apply(ProjectResource project, DeleteBranchesInput input)
 | 
			
		||||
      throws OrmException, IOException, ResourceConflictException {
 | 
			
		||||
    input = Input.init(input);
 | 
			
		||||
 | 
			
		||||
    if (input == null) {
 | 
			
		||||
      input = new DeleteBranchesInput();
 | 
			
		||||
    }
 | 
			
		||||
    if (input.branches == null) {
 | 
			
		||||
      input.branches = Lists.newArrayListWithCapacity(1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    try (Repository r = repoManager.openRepository(project.getNameKey())) {
 | 
			
		||||
      BatchRefUpdate batchUpdate = r.getRefDatabase().newBatchUpdate();
 | 
			
		||||
      for (String branch : input.branches) {
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user