Support deleting vote on a revision
Right now deleting a vote is done by:
'DELETE /changes/{change-id}/reviewers/{account-id}/votes/{label-id}'
A new REST endpoint is added as:
'DELETE /changes/{change-id}/revisions/{revision-id}/reviewers/{account-id}/votes/{label-id}'
This new endpoint can help prevent deleting the vote (with same label)
from a newer patch set by mistake.
'List Revision Reviewers' and 'List Revision Votes' endpoints are also
added as by-products. All of the three endpoints are limited on current
revision.
Change-Id: I85fac04db986c2133cd39d2d06648d1dad8c3b4c
This commit is contained in:
@@ -4762,6 +4762,131 @@ describes the resulting cherry picked change.
|
||||
}
|
||||
----
|
||||
|
||||
[[revision-reviewer-endpoints]]
|
||||
== Revision Reviewer Endpoints
|
||||
|
||||
[[list-revision-reviewers]]
|
||||
=== List Revision Reviewers
|
||||
--
|
||||
'GET /changes/link:#change-id[\{change-id\}]/revisions/link:#revision-id[\{revision-id\}]/reviewers/'
|
||||
--
|
||||
|
||||
Lists the reviewers of a revision.
|
||||
|
||||
Please note that only the current revision is supported.
|
||||
|
||||
As result a list of link:#reviewer-info[ReviewerInfo] entries is returned.
|
||||
|
||||
.Request
|
||||
----
|
||||
GET /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/revisions/674ac754f91e64a0efb8087e59a176484bd534d1/reviewers/ HTTP/1.0
|
||||
----
|
||||
|
||||
.Response
|
||||
----
|
||||
HTTP/1.1 200 OK
|
||||
Content-Disposition: attachment
|
||||
Content-Type: application/json; charset=UTF-8
|
||||
|
||||
)]}'
|
||||
[
|
||||
{
|
||||
"approvals": {
|
||||
"Verified": "+1",
|
||||
"Code-Review": "+2"
|
||||
},
|
||||
"_account_id": 1000096,
|
||||
"name": "John Doe",
|
||||
"email": "john.doe@example.com"
|
||||
},
|
||||
{
|
||||
"approvals": {
|
||||
"Verified": " 0",
|
||||
"Code-Review": "-1"
|
||||
},
|
||||
"_account_id": 1000097,
|
||||
"name": "Jane Roe",
|
||||
"email": "jane.roe@example.com"
|
||||
}
|
||||
]
|
||||
----
|
||||
|
||||
[[list-revision-votes]]
|
||||
=== List Revision Votes
|
||||
--
|
||||
'GET /changes/link:#change-id[\{change-id\}]/revisions/link:#revision-id[\{revision-id\}]/reviewers/link:rest-api-accounts.html#account-id[\{account-id\}]/votes/'
|
||||
--
|
||||
|
||||
Lists the votes for a specific reviewer of the revision.
|
||||
|
||||
Please note that only the current revision is supported.
|
||||
|
||||
.Request
|
||||
----
|
||||
GET /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/revisions/674ac754f91e64a0efb8087e59a176484bd534d1/reviewers/John%20Doe/votes/ HTTP/1.0
|
||||
----
|
||||
|
||||
As result a map is returned that maps the label name to the label value.
|
||||
The entries in the map are sorted by label name.
|
||||
|
||||
.Response
|
||||
----
|
||||
HTTP/1.1 200 OK
|
||||
Content-Disposition: attachment
|
||||
Content-Type: application/json;charset=UTF-8
|
||||
|
||||
)]}'
|
||||
{
|
||||
"Code-Review": -1,
|
||||
"Verified": 1,
|
||||
"Work-In-Progress": 1
|
||||
}
|
||||
----
|
||||
|
||||
[[delete-revision-vote]]
|
||||
=== Delete Revision Vote
|
||||
--
|
||||
'DELETE /changes/link:#change-id[\{change-id\}]/revisions/link:#revision-id[\{revision-id\}]
|
||||
/reviewers/link:rest-api-accounts.html#account-id[\{account-id\}]/votes/link:#label-id[\{label-id\}]' +
|
||||
'POST /changes/link:#change-id[\{change-id\}]/revisions/link:#revision-id[\{revision-id\}]
|
||||
/reviewers/link:rest-api-accounts.html#account-id[\{account-id\}]/votes/link:#label-id[\{label-id\}]/delete'
|
||||
--
|
||||
|
||||
Deletes a single vote from a revision. The deletion will be possible only
|
||||
if the revision is the current revision. By using this endpoint you can prevent
|
||||
deleting the vote (with same label) from a newer patch set by mistake.
|
||||
|
||||
Note, that even when the last vote of a reviewer is removed the reviewer itself
|
||||
is still listed on the change.
|
||||
|
||||
Options can be provided in the request body as a
|
||||
link:#delete-vote-input[DeleteVoteInput] entity.
|
||||
|
||||
.Request
|
||||
----
|
||||
DELETE /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/revisions/674ac754f91e64a0efb8087e59a176484bd534d1/reviewers/John%20Doe/votes/Code-Review HTTP/1.0
|
||||
POST /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/revisions/674ac754f91e64a0efb8087e59a176484bd534d1/reviewers/John%20Doe/votes/Code-Review/delete HTTP/1.0
|
||||
----
|
||||
|
||||
Please note that some proxies prohibit request bodies for DELETE
|
||||
requests. In this case, if you want to specify options, use a POST
|
||||
request:
|
||||
|
||||
.Request
|
||||
----
|
||||
POST /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/revisions/674ac754f91e64a0efb8087e59a176484bd534d1/reviewers/John%20Doe/votes/Code-Review/delete HTTP/1.0
|
||||
Content-Type: application/json; charset=UTF-8
|
||||
|
||||
{
|
||||
"notify": "NONE"
|
||||
}
|
||||
----
|
||||
|
||||
.Response
|
||||
----
|
||||
HTTP/1.1 204 No Content
|
||||
----
|
||||
|
||||
[[ids]]
|
||||
== IDs
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ import static com.google.gerrit.acceptance.PushOneCommit.PATCH;
|
||||
import static com.google.gerrit.acceptance.PushOneCommit.PATCH_FILE_ONLY;
|
||||
import static com.google.gerrit.acceptance.PushOneCommit.SUBJECT;
|
||||
import static com.google.gerrit.extensions.client.ListChangesOption.DETAILED_LABELS;
|
||||
import static com.google.gerrit.extensions.client.ReviewerState.REVIEWER;
|
||||
import static com.google.gerrit.reviewdb.client.Patch.COMMIT_MSG;
|
||||
import static com.google.gerrit.reviewdb.client.Patch.MERGE_LIST;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
@@ -30,6 +31,7 @@ import static org.junit.Assert.fail;
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Iterators;
|
||||
import com.google.gerrit.acceptance.AbstractDaemonTest;
|
||||
@@ -46,6 +48,7 @@ import com.google.gerrit.extensions.api.changes.RevisionApi;
|
||||
import com.google.gerrit.extensions.api.projects.BranchInput;
|
||||
import com.google.gerrit.extensions.client.ChangeStatus;
|
||||
import com.google.gerrit.extensions.client.SubmitType;
|
||||
import com.google.gerrit.extensions.common.AccountInfo;
|
||||
import com.google.gerrit.extensions.common.ApprovalInfo;
|
||||
import com.google.gerrit.extensions.common.ChangeInfo;
|
||||
import com.google.gerrit.extensions.common.ChangeMessageInfo;
|
||||
@@ -58,8 +61,10 @@ import com.google.gerrit.extensions.common.RevisionInfo;
|
||||
import com.google.gerrit.extensions.restapi.BadRequestException;
|
||||
import com.google.gerrit.extensions.restapi.BinaryResult;
|
||||
import com.google.gerrit.extensions.restapi.ETagView;
|
||||
import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
|
||||
import com.google.gerrit.extensions.restapi.ResourceConflictException;
|
||||
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
|
||||
import com.google.gerrit.reviewdb.client.Account;
|
||||
import com.google.gerrit.reviewdb.client.Branch;
|
||||
import com.google.gerrit.reviewdb.client.Change;
|
||||
import com.google.gerrit.reviewdb.client.PatchSetApproval;
|
||||
@@ -1053,6 +1058,71 @@ public class RevisionIT extends AbstractDaemonTest {
|
||||
oldETag = checkETag(getRevisionActions, r2, oldETag);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deleteVoteOnNonCurrentPatchSet() throws Exception {
|
||||
PushOneCommit.Result r = createChange(); // patch set 1
|
||||
gApi.changes()
|
||||
.id(r.getChangeId())
|
||||
.revision(r.getCommit().name())
|
||||
.review(ReviewInput.approve());
|
||||
|
||||
// patch set 2
|
||||
amendChange(r.getChangeId());
|
||||
|
||||
// code-review
|
||||
setApiUser(user);
|
||||
recommend(r.getChangeId());
|
||||
|
||||
// check if it's blocked to delete a vote on a non-current patch set.
|
||||
exception.expect(MethodNotAllowedException.class);
|
||||
exception.expectMessage("Cannot access on non-current patch set");
|
||||
setApiUser(admin);
|
||||
gApi.changes()
|
||||
.id(r.getChangeId())
|
||||
.revision(r.getCommit().getName())
|
||||
.reviewer(user.getId().toString())
|
||||
.deleteVote("Code-Review");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deleteVoteOnCurrentPatchSet() throws Exception {
|
||||
PushOneCommit.Result r = createChange(); // patch set 1
|
||||
gApi.changes()
|
||||
.id(r.getChangeId())
|
||||
.revision(r.getCommit().name())
|
||||
.review(ReviewInput.approve());
|
||||
|
||||
// patch set 2
|
||||
amendChange(r.getChangeId());
|
||||
|
||||
// code-review
|
||||
setApiUser(user);
|
||||
recommend(r.getChangeId());
|
||||
|
||||
setApiUser(admin);
|
||||
gApi.changes()
|
||||
.id(r.getChangeId())
|
||||
.current()
|
||||
.reviewer(user.getId().toString())
|
||||
.deleteVote("Code-Review");
|
||||
|
||||
Map<String, Short> m = gApi.changes()
|
||||
.id(r.getChangeId())
|
||||
.current()
|
||||
.reviewer(user.getId().toString())
|
||||
.votes();
|
||||
|
||||
assertThat(m).containsExactly("Code-Review", Short.valueOf((short)0));
|
||||
|
||||
ChangeInfo c = gApi.changes().id(r.getChangeId()).get();
|
||||
ChangeMessageInfo message = Iterables.getLast(c.messages);
|
||||
assertThat(message.author._accountId).isEqualTo(admin.getId().get());
|
||||
assertThat(message.message).isEqualTo(
|
||||
"Removed Code-Review+1 by User <user@example.com>\n");
|
||||
assertThat(getReviewers(c.reviewers.get(REVIEWER)))
|
||||
.containsExactlyElementsIn(ImmutableSet.of(admin.getId(), user.getId()));
|
||||
}
|
||||
|
||||
private PushOneCommit.Result updateChange(PushOneCommit.Result r,
|
||||
String content) throws Exception {
|
||||
PushOneCommit push = pushFactory.create(db, admin.getIdent(), testRepo,
|
||||
@@ -1178,4 +1248,9 @@ public class RevisionIT extends AbstractDaemonTest {
|
||||
.findFirst()
|
||||
.get();
|
||||
}
|
||||
|
||||
private static Iterable<Account.Id> getReviewers(
|
||||
Collection<AccountInfo> r) {
|
||||
return Iterables.transform(r, a -> new Account.Id(a._accountId));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@ public interface RevisionApi {
|
||||
ChangeApi rebase(RebaseInput in) throws RestApiException;
|
||||
boolean canRebase() throws RestApiException;
|
||||
|
||||
RevisionReviewerApi reviewer(String id) throws RestApiException;
|
||||
void setReviewed(String path, boolean reviewed) throws RestApiException;
|
||||
Set<String> reviewed() throws RestApiException;
|
||||
|
||||
@@ -162,6 +163,11 @@ public interface RevisionApi {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RevisionReviewerApi reviewer(String id) throws RestApiException {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReviewed(String path, boolean reviewed) throws RestApiException {
|
||||
throw new NotImplementedException();
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
// Copyright (C) 2017 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.changes;
|
||||
|
||||
import com.google.gerrit.extensions.restapi.NotImplementedException;
|
||||
import com.google.gerrit.extensions.restapi.RestApiException;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
public interface RevisionReviewerApi {
|
||||
Map<String, Short> votes() throws RestApiException;
|
||||
|
||||
void deleteVote(String label) throws RestApiException;
|
||||
|
||||
void deleteVote(DeleteVoteInput input) throws RestApiException;
|
||||
|
||||
/**
|
||||
* A default implementation which allows source compatibility
|
||||
* when adding new methods to the interface.
|
||||
**/
|
||||
class NotImplemented implements RevisionReviewerApi {
|
||||
@Override
|
||||
public Map<String, Short> votes() throws RestApiException {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteVote(String label) throws RestApiException {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteVote(DeleteVoteInput input) throws RestApiException {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -29,5 +29,6 @@ public class Module extends FactoryModule {
|
||||
factory(RevisionApiImpl.Factory.class);
|
||||
factory(FileApiImpl.Factory.class);
|
||||
factory(ReviewerApiImpl.Factory.class);
|
||||
factory(RevisionReviewerApiImpl.Factory.class);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import com.google.gerrit.extensions.api.changes.FileApi;
|
||||
import com.google.gerrit.extensions.api.changes.RebaseInput;
|
||||
import com.google.gerrit.extensions.api.changes.ReviewInput;
|
||||
import com.google.gerrit.extensions.api.changes.RevisionApi;
|
||||
import com.google.gerrit.extensions.api.changes.RevisionReviewerApi;
|
||||
import com.google.gerrit.extensions.api.changes.RobotCommentApi;
|
||||
import com.google.gerrit.extensions.api.changes.SubmitInput;
|
||||
import com.google.gerrit.extensions.client.SubmitType;
|
||||
@@ -63,6 +64,7 @@ import com.google.gerrit.server.change.Rebase;
|
||||
import com.google.gerrit.server.change.RebaseUtil;
|
||||
import com.google.gerrit.server.change.Reviewed;
|
||||
import com.google.gerrit.server.change.RevisionResource;
|
||||
import com.google.gerrit.server.change.RevisionReviewers;
|
||||
import com.google.gerrit.server.change.RobotComments;
|
||||
import com.google.gerrit.server.change.Submit;
|
||||
import com.google.gerrit.server.change.TestSubmitType;
|
||||
@@ -89,6 +91,8 @@ class RevisionApiImpl implements RevisionApi {
|
||||
|
||||
private final GitRepositoryManager repoManager;
|
||||
private final Changes changes;
|
||||
private final RevisionReviewers revisionReviewers;
|
||||
private final RevisionReviewerApiImpl.Factory revisionReviewerApi;
|
||||
private final CherryPick cherryPick;
|
||||
private final DeleteDraftPatchSet deleteDraft;
|
||||
private final Rebase rebase;
|
||||
@@ -125,6 +129,8 @@ class RevisionApiImpl implements RevisionApi {
|
||||
@Inject
|
||||
RevisionApiImpl(GitRepositoryManager repoManager,
|
||||
Changes changes,
|
||||
RevisionReviewers revisionReviewers,
|
||||
RevisionReviewerApiImpl.Factory revisionReviewerApi,
|
||||
CherryPick cherryPick,
|
||||
DeleteDraftPatchSet deleteDraft,
|
||||
Rebase rebase,
|
||||
@@ -159,6 +165,8 @@ class RevisionApiImpl implements RevisionApi {
|
||||
@Assisted RevisionResource r) {
|
||||
this.repoManager = repoManager;
|
||||
this.changes = changes;
|
||||
this.revisionReviewers = revisionReviewers;
|
||||
this.revisionReviewerApi = revisionReviewerApi;
|
||||
this.cherryPick = cherryPick;
|
||||
this.deleteDraft = deleteDraft;
|
||||
this.rebase = rebase;
|
||||
@@ -281,6 +289,16 @@ class RevisionApiImpl implements RevisionApi {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public RevisionReviewerApi reviewer(String id) throws RestApiException {
|
||||
try {
|
||||
return revisionReviewerApi.create(
|
||||
revisionReviewers.parse(revision, IdString.fromDecoded(id)));
|
||||
} catch (OrmException e) {
|
||||
throw new RestApiException("Cannot parse reviewer", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReviewed(String path, boolean reviewed) throws RestApiException {
|
||||
try {
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
// Copyright (C) 2017 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.api.changes;
|
||||
|
||||
import com.google.gerrit.extensions.api.changes.DeleteVoteInput;
|
||||
import com.google.gerrit.extensions.api.changes.RevisionReviewerApi;
|
||||
import com.google.gerrit.extensions.restapi.RestApiException;
|
||||
import com.google.gerrit.server.change.DeleteVote;
|
||||
import com.google.gerrit.server.change.ReviewerResource;
|
||||
import com.google.gerrit.server.change.VoteResource;
|
||||
import com.google.gerrit.server.change.Votes;
|
||||
import com.google.gerrit.server.git.UpdateException;
|
||||
import com.google.gwtorm.server.OrmException;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.assistedinject.Assisted;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class RevisionReviewerApiImpl implements RevisionReviewerApi {
|
||||
interface Factory {
|
||||
RevisionReviewerApiImpl create(ReviewerResource r);
|
||||
}
|
||||
|
||||
private final ReviewerResource reviewer;
|
||||
private final Votes.List listVotes;
|
||||
private final DeleteVote deleteVote;
|
||||
|
||||
@Inject
|
||||
RevisionReviewerApiImpl(Votes.List listVotes,
|
||||
DeleteVote deleteVote,
|
||||
@Assisted ReviewerResource reviewer) {
|
||||
this.listVotes = listVotes;
|
||||
this.deleteVote = deleteVote;
|
||||
this.reviewer = reviewer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Short> votes() throws RestApiException {
|
||||
try {
|
||||
return listVotes.apply(reviewer);
|
||||
} catch (OrmException e) {
|
||||
throw new RestApiException("Cannot list votes", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteVote(String label) throws RestApiException {
|
||||
try {
|
||||
deleteVote.apply(new VoteResource(reviewer, label), null);
|
||||
} catch (UpdateException e) {
|
||||
throw new RestApiException("Cannot delete vote", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteVote(DeleteVoteInput input) throws RestApiException {
|
||||
try {
|
||||
deleteVote.apply(new VoteResource(reviewer, input.label), input);
|
||||
} catch (UpdateException e) {
|
||||
throw new RestApiException("Cannot delete vote", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,7 @@ import com.google.gerrit.extensions.api.changes.DeleteVoteInput;
|
||||
import com.google.gerrit.extensions.api.changes.NotifyHandling;
|
||||
import com.google.gerrit.extensions.restapi.AuthException;
|
||||
import com.google.gerrit.extensions.restapi.BadRequestException;
|
||||
import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
|
||||
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
|
||||
import com.google.gerrit.extensions.restapi.Response;
|
||||
import com.google.gerrit.extensions.restapi.RestApiException;
|
||||
@@ -108,6 +109,13 @@ public class DeleteVote
|
||||
}
|
||||
ReviewerResource r = rsrc.getReviewer();
|
||||
Change change = r.getChange();
|
||||
|
||||
if (r.getRevisionResource() != null
|
||||
&& !r.getRevisionResource().isCurrent()) {
|
||||
throw new MethodNotAllowedException(
|
||||
"Cannot delete vote on non-current patch set");
|
||||
}
|
||||
|
||||
try (BatchUpdate bu = batchUpdateFactory.create(db.get(),
|
||||
change.getProject(), r.getControl().getUser(), TimeUtil.nowTs())) {
|
||||
bu.addOp(change.getId(),
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
// Copyright (C) 2017 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.change;
|
||||
|
||||
import com.google.gerrit.extensions.api.changes.ReviewerInfo;
|
||||
import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
|
||||
import com.google.gerrit.extensions.restapi.RestReadView;
|
||||
import com.google.gerrit.reviewdb.client.Account;
|
||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||
import com.google.gerrit.server.ApprovalsUtil;
|
||||
import com.google.gwtorm.server.OrmException;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Singleton
|
||||
class ListRevisionReviewers implements RestReadView<RevisionResource> {
|
||||
private final Provider<ReviewDb> dbProvider;
|
||||
private final ApprovalsUtil approvalsUtil;
|
||||
private final ReviewerJson json;
|
||||
private final ReviewerResource.Factory resourceFactory;
|
||||
|
||||
@Inject
|
||||
ListRevisionReviewers(Provider<ReviewDb> dbProvider,
|
||||
ApprovalsUtil approvalsUtil,
|
||||
ReviewerResource.Factory resourceFactory,
|
||||
ReviewerJson json) {
|
||||
this.dbProvider = dbProvider;
|
||||
this.approvalsUtil = approvalsUtil;
|
||||
this.resourceFactory = resourceFactory;
|
||||
this.json = json;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ReviewerInfo> apply(RevisionResource rsrc) throws OrmException,
|
||||
MethodNotAllowedException {
|
||||
if (!rsrc.isCurrent()) {
|
||||
throw new MethodNotAllowedException(
|
||||
"Cannot list reviewers on non-current patch set");
|
||||
}
|
||||
|
||||
Map<Account.Id, ReviewerResource> reviewers = new LinkedHashMap<>();
|
||||
ReviewDb db = dbProvider.get();
|
||||
for (Account.Id accountId
|
||||
: approvalsUtil.getReviewers(db, rsrc.getNotes()).all()) {
|
||||
if (!reviewers.containsKey(accountId)) {
|
||||
reviewers.put(accountId, resourceFactory.create(rsrc, accountId));
|
||||
}
|
||||
}
|
||||
return json.format(reviewers.values());
|
||||
}
|
||||
}
|
||||
@@ -36,6 +36,7 @@ public class Module extends RestApiModule {
|
||||
bind(ChangesCollection.class);
|
||||
bind(Revisions.class);
|
||||
bind(Reviewers.class);
|
||||
bind(RevisionReviewers.class);
|
||||
bind(DraftComments.class);
|
||||
bind(Comments.class);
|
||||
bind(RobotComments.class);
|
||||
@@ -114,6 +115,8 @@ public class Module extends RestApiModule {
|
||||
get(REVISION_KIND, "archive").to(GetArchive.class);
|
||||
get(REVISION_KIND, "mergelist").to(GetMergeList.class);
|
||||
|
||||
child(REVISION_KIND, "reviewers").to(RevisionReviewers.class);
|
||||
|
||||
child(REVISION_KIND, "drafts").to(DraftComments.class);
|
||||
put(REVISION_KIND, "drafts").to(CreateDraftComment.class);
|
||||
get(DRAFT_COMMENT_KIND).to(GetDraftComment.class);
|
||||
|
||||
@@ -30,9 +30,11 @@ public class ReviewerResource implements RestResource {
|
||||
|
||||
public interface Factory {
|
||||
ReviewerResource create(ChangeResource change, Account.Id id);
|
||||
ReviewerResource create(RevisionResource revision, Account.Id id);
|
||||
}
|
||||
|
||||
private final ChangeResource change;
|
||||
private final RevisionResource revision;
|
||||
private final IdentifiedUser user;
|
||||
|
||||
@AssistedInject
|
||||
@@ -40,6 +42,16 @@ public class ReviewerResource implements RestResource {
|
||||
@Assisted ChangeResource change,
|
||||
@Assisted Account.Id id) {
|
||||
this.change = change;
|
||||
this.revision = null;
|
||||
this.user = userFactory.create(id);
|
||||
}
|
||||
|
||||
@AssistedInject
|
||||
ReviewerResource(IdentifiedUser.GenericFactory userFactory,
|
||||
@Assisted RevisionResource revision,
|
||||
@Assisted Account.Id id) {
|
||||
this.revision = revision;
|
||||
this.change = revision.getChangeResource();
|
||||
this.user = userFactory.create(id);
|
||||
}
|
||||
|
||||
@@ -47,6 +59,10 @@ public class ReviewerResource implements RestResource {
|
||||
return change;
|
||||
}
|
||||
|
||||
public RevisionResource getRevisionResource() {
|
||||
return revision;
|
||||
}
|
||||
|
||||
public Change.Id getChangeId() {
|
||||
return change.getId();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
// Copyright (C) 2017 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.change;
|
||||
|
||||
import com.google.gerrit.extensions.registration.DynamicMap;
|
||||
import com.google.gerrit.extensions.restapi.AuthException;
|
||||
import com.google.gerrit.extensions.restapi.ChildCollection;
|
||||
import com.google.gerrit.extensions.restapi.IdString;
|
||||
import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
|
||||
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
|
||||
import com.google.gerrit.extensions.restapi.RestView;
|
||||
import com.google.gerrit.extensions.restapi.TopLevelResource;
|
||||
import com.google.gerrit.reviewdb.client.Account;
|
||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||
import com.google.gerrit.server.ApprovalsUtil;
|
||||
import com.google.gerrit.server.account.AccountsCollection;
|
||||
import com.google.gwtorm.server.OrmException;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
@Singleton
|
||||
public class RevisionReviewers implements
|
||||
ChildCollection<RevisionResource, ReviewerResource> {
|
||||
private final DynamicMap<RestView<ReviewerResource>> views;
|
||||
private final Provider<ReviewDb> dbProvider;
|
||||
private final ApprovalsUtil approvalsUtil;
|
||||
private final AccountsCollection accounts;
|
||||
private final ReviewerResource.Factory resourceFactory;
|
||||
private final ListRevisionReviewers list;
|
||||
|
||||
@Inject
|
||||
RevisionReviewers(Provider<ReviewDb> dbProvider,
|
||||
ApprovalsUtil approvalsUtil,
|
||||
AccountsCollection accounts,
|
||||
ReviewerResource.Factory resourceFactory,
|
||||
DynamicMap<RestView<ReviewerResource>> views,
|
||||
ListRevisionReviewers list) {
|
||||
this.dbProvider = dbProvider;
|
||||
this.approvalsUtil = approvalsUtil;
|
||||
this.accounts = accounts;
|
||||
this.resourceFactory = resourceFactory;
|
||||
this.views = views;
|
||||
this.list = list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DynamicMap<RestView<ReviewerResource>> views() {
|
||||
return views;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RestView<RevisionResource> list() {
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReviewerResource parse(RevisionResource rsrc, IdString id)
|
||||
throws OrmException, ResourceNotFoundException, AuthException,
|
||||
MethodNotAllowedException {
|
||||
if (!rsrc.isCurrent()) {
|
||||
throw new MethodNotAllowedException(
|
||||
"Cannot access on non-current patch set");
|
||||
}
|
||||
|
||||
Account.Id accountId =
|
||||
accounts.parse(TopLevelResource.INSTANCE, id).getUser().getAccountId();
|
||||
|
||||
Collection<Account.Id> reviewers = approvalsUtil.getReviewers(
|
||||
dbProvider.get(), rsrc.getNotes()).all();
|
||||
if (reviewers.contains(accountId)) {
|
||||
return resourceFactory.create(rsrc, accountId);
|
||||
}
|
||||
throw new ResourceNotFoundException(id);
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@ import com.google.gerrit.extensions.registration.DynamicMap;
|
||||
import com.google.gerrit.extensions.restapi.AuthException;
|
||||
import com.google.gerrit.extensions.restapi.ChildCollection;
|
||||
import com.google.gerrit.extensions.restapi.IdString;
|
||||
import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
|
||||
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
|
||||
import com.google.gerrit.extensions.restapi.RestReadView;
|
||||
import com.google.gerrit.extensions.restapi.RestView;
|
||||
@@ -56,7 +57,13 @@ public class Votes implements ChildCollection<ReviewerResource, VoteResource> {
|
||||
|
||||
@Override
|
||||
public VoteResource parse(ReviewerResource reviewer, IdString id)
|
||||
throws ResourceNotFoundException, OrmException, AuthException {
|
||||
throws ResourceNotFoundException, OrmException, AuthException,
|
||||
MethodNotAllowedException {
|
||||
if (!reviewer.getRevisionResource().isCurrent()) {
|
||||
throw new MethodNotAllowedException(
|
||||
"Cannot access on non-current patch set");
|
||||
}
|
||||
|
||||
return new VoteResource(reviewer, id.get());
|
||||
}
|
||||
|
||||
@@ -73,7 +80,14 @@ public class Votes implements ChildCollection<ReviewerResource, VoteResource> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Short> apply(ReviewerResource rsrc) throws OrmException {
|
||||
public Map<String, Short> apply(ReviewerResource rsrc)
|
||||
throws OrmException, MethodNotAllowedException {
|
||||
if (rsrc.getRevisionResource() != null
|
||||
&& !rsrc.getRevisionResource().isCurrent()) {
|
||||
throw new MethodNotAllowedException(
|
||||
"Cannot list votes on non-current patch set");
|
||||
}
|
||||
|
||||
Map<String, Short> votes = new TreeMap<>();
|
||||
Iterable<PatchSetApproval> byPatchSetUser = approvalsUtil.byPatchSetUser(
|
||||
db.get(),
|
||||
|
||||
Reference in New Issue
Block a user