Add REST endpoint to ban commits

Change-Id: I941f76be00b9bce7f6f6f26925a82efe76fa42b5
Signed-off-by: Edwin Kempin <edwin.kempin@sap.com>
This commit is contained in:
Edwin Kempin
2014-07-09 11:17:58 +02:00
committed by David Pursehouse
parent e6d550e559
commit 62946749c8
4 changed files with 265 additions and 0 deletions

View File

@@ -789,6 +789,57 @@ The response is the streamed output of the garbage collection.
done.
----
[[ban-commit]]
=== Ban Commit
--
'PUT /projects/link:#project-name[\{project-name\}]/ban'
--
Marks commits as banned for the project. If a commit is banned Gerrit
rejects every push that includes this commit with
link:error-contains-banned-commit.html[contains banned commit ...].
[NOTE]
This REST endpoint only marks the commits as banned, but it does not
remove the commits from the history of any central branch. This needs
to be done manually.
The commits to be banned must be specified in the request body as a
link:#ban-input[BanInput] entity.
The caller must be project owner.
.Request
----
PUT /projects/plugins%2Freplication/ban HTTP/1.0
Content-Type: application/json;charset=UTF-8
{
"commits": [
"a8a477efffbbf3b44169bb9a1d3a334cbbd9aa96",
"cf5b56541f84b8b57e16810b18daca9c3adc377b"
],
"reason": "Violates IP"
}
----
As response a link:#ban-result-info[BanResultInfo] entity is returned.
.Response
----
HTTP/1.1 200 OK
Content-Disposition: attachment
Content-Type: application/json;charset=UTF-8
)]}'
{
"newly_banned": [
"a8a477efffbbf3b44169bb9a1d3a334cbbd9aa96",
"cf5b56541f84b8b57e16810b18daca9c3adc377b"
]
}
----
[[branch-endpoints]]
== Branch Endpoints
@@ -1492,6 +1543,30 @@ The `BranchInfo` entity contains information about a branch.
Whether the calling user can delete this branch.
|=========================
[[ban-input]]
=== BanInput
The `BanInput` entity contains information for banning commits in a
project.
[options="header",width="50%",cols="1,^2,4"]
|=======================
|Field Name||Description
|`commits` ||List of commits to be banned.
|`reason` |optional|Reason for banning the commits.
|=======================
[[ban-result-info]]
=== BanResultInfo
The `BanResultInfo` entity describes the result of banning commits.
[options="header",width="50%",cols="1,^2,4"]
|=============================
|Field Name ||Description
|`newly_banned` |optional|List of newly banned commits.
|`already_banned`|optional|List of commits that were already banned.
|`ignored` |optional|List of object IDs that were ignored.
|=============================
[[branch-input]]
=== BranchInput
The `BranchInput` entity contains information for the creation of

View File

@@ -0,0 +1,82 @@
// Copyright (C) 2014 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.GitUtil.add;
import static com.google.gerrit.acceptance.GitUtil.createCommit;
import static com.google.gerrit.acceptance.GitUtil.pushHead;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import com.google.common.collect.Iterables;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.GitUtil.Commit;
import com.google.gerrit.acceptance.RestResponse;
import com.google.gerrit.server.project.BanCommit;
import com.google.gerrit.server.project.BanCommit.BanResultInfo;
import org.apache.http.HttpStatus;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.transport.PushResult;
import org.junit.Test;
import java.io.IOException;
public class BanCommitIT extends AbstractDaemonTest {
@Test
public void banCommit() throws IOException, GitAPIException {
add(git, "a.txt", "some content");
Commit c = createCommit(git, admin.getIdent(), "subject");
RestResponse r =
adminSession.put("/projects/" + project.get() + "/ban/",
BanCommit.Input.fromCommits(c.getCommit().getName()));
assertEquals(HttpStatus.SC_OK, r.getStatusCode());
BanResultInfo info = newGson().fromJson(r.getReader(), BanResultInfo.class);
assertEquals(c.getCommit().getName(), Iterables.getOnlyElement(info.newlyBanned));
assertNull(info.alreadyBanned);
assertNull(info.ignored);
PushResult pushResult = pushHead(git, "refs/heads/master", false);
assertTrue(pushResult.getRemoteUpdate("refs/heads/master").getMessage()
.startsWith("contains banned commit"));
}
@Test
public void banAlreadyBannedCommit() throws IOException, GitAPIException {
RestResponse r =
adminSession.put("/projects/" + project.get() + "/ban/",
BanCommit.Input.fromCommits("a8a477efffbbf3b44169bb9a1d3a334cbbd9aa96"));
r.consume();
r = adminSession.put("/projects/" + project.get() + "/ban/",
BanCommit.Input.fromCommits("a8a477efffbbf3b44169bb9a1d3a334cbbd9aa96"));
assertEquals(HttpStatus.SC_OK, r.getStatusCode());
BanResultInfo info = newGson().fromJson(r.getReader(), BanResultInfo.class);
assertEquals("a8a477efffbbf3b44169bb9a1d3a334cbbd9aa96", Iterables.getOnlyElement(info.alreadyBanned));
assertNull(info.newlyBanned);
assertNull(info.ignored);
}
@Test
public void banCommit_Forbidden() throws IOException {
RestResponse r =
userSession.put("/projects/" + project.get() + "/ban/",
BanCommit.Input.fromCommits("a8a477efffbbf3b44169bb9a1d3a334cbbd9aa96"));
assertEquals(HttpStatus.SC_FORBIDDEN, r.getStatusCode());
}
}

View File

@@ -0,0 +1,106 @@
// Copyright (C) 2014 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.server.project;
import com.google.common.base.Function;
import com.google.common.collect.Lists;
import com.google.gerrit.common.errors.PermissionDeniedException;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
import com.google.gerrit.server.git.BanCommitResult;
import com.google.gerrit.server.git.MergeException;
import com.google.gerrit.server.project.BanCommit.Input;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import org.eclipse.jgit.api.errors.ConcurrentRefUpdateException;
import org.eclipse.jgit.lib.ObjectId;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@Singleton
public class BanCommit implements RestModifyView<ProjectResource, Input> {
public static class Input {
public List<String> commits;
public String reason;
public static Input fromCommits(String firstCommit, String... moreCommits) {
return fromCommits(Lists.asList(firstCommit, moreCommits));
}
public static Input fromCommits(List<String> commits) {
Input in = new Input();
in.commits = commits;
return in;
}
}
@Inject
private com.google.gerrit.server.git.BanCommit banCommit;
@Override
public BanResultInfo apply(ProjectResource rsrc, Input input)
throws UnprocessableEntityException, AuthException,
ResourceConflictException, IOException, InterruptedException {
BanResultInfo r = new BanResultInfo();
if (input != null && input.commits != null && !input.commits.isEmpty()) {
List<ObjectId> commitsToBan = new ArrayList<>(input.commits.size());
for (String c : input.commits) {
try {
commitsToBan.add(ObjectId.fromString(c));
} catch (IllegalArgumentException e) {
throw new UnprocessableEntityException(e.getMessage());
}
}
try {
BanCommitResult result =
banCommit.ban(rsrc.getControl(), commitsToBan, input.reason);
r.newlyBanned = transformCommits(result.getNewlyBannedCommits());
r.alreadyBanned = transformCommits(result.getAlreadyBannedCommits());
r.ignored = transformCommits(result.getIgnoredObjectIds());
} catch (PermissionDeniedException e) {
throw new AuthException(e.getMessage());
} catch (MergeException | ConcurrentRefUpdateException e) {
throw new ResourceConflictException(e.getMessage(), e);
}
}
return r;
}
private static List<String> transformCommits(List<ObjectId> commits) {
if (commits == null || commits.isEmpty()) {
return null;
}
return Lists.transform(commits,
new Function<ObjectId, String>() {
@Override
public String apply(ObjectId id) {
return id.getName();
}
});
}
public static class BanResultInfo {
public List<String> newlyBanned;
public List<String> alreadyBanned;
public List<String> ignored;
}
}

View File

@@ -53,6 +53,8 @@ public class Module extends RestApiModule {
get(PROJECT_KIND, "HEAD").to(GetHead.class);
put(PROJECT_KIND, "HEAD").to(SetHead.class);
put(PROJECT_KIND, "ban").to(BanCommit.class);
get(PROJECT_KIND, "statistics.git").to(GetStatistics.class);
post(PROJECT_KIND, "gc").to(GarbageCollect.class);