Update Set Review API to behave in line with RFC 7231

This CL updates the implementation of PostReview to replace
all instances of raising UnprocessableEntityException
(HTTP 422) with failed Additions instead. This matches the
docs for Set Reviewer, which no longer mention 422 being a
valid response code. This also falls in line with
RFC 7231, which promotes HTTP 400s with detailed bodies
over 422s.

It also updates the documentation to be clearer about
how these API functions work.

Bug: issue 6016
Change-Id: I1adad98107ba021132293ec47c6a6da01787556e
This commit is contained in:
Aaron Gable
2017-04-25 12:03:37 -07:00
committed by David Pursehouse
parent 0beb6d9540
commit 8c650215eb
9 changed files with 234 additions and 190 deletions

View File

@@ -2745,16 +2745,16 @@ returned that describes the newly added reviewers.
)]}'
{
"input": "john.doe@example.com",
"reviewers": [
{
"input": "john.doe@example.com",
"_account_id": 1000096,
"name": "John Doe",
"email": "john.doe@example.com"
"approvals": {
"Verified": " 0",
"Code-Review": " 0"
},
"_account_id": 1000096,
"name": "John Doe",
"email": "john.doe@example.com"
}
]
}
@@ -3387,6 +3387,11 @@ Sets a review on a revision.
The review must be provided in the request body as a
link:#review-input[ReviewInput] entity.
A review cannot be set on a change edit. Trying to post a review for a
change edit fails with `409 Conflict`.
This API method can be used to set labels:
.Request
----
POST /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/revisions/674ac754f91e64a0efb8087e59a176484bd534d1/review HTTP/1.0
@@ -3423,7 +3428,8 @@ link:#review-input[ReviewInput] entity.
----
As response a link:#review-result[ReviewResult] entity is returned that
describes the applied labels.
describes the applied labels and any added reviewers (e.g. yourself,
if you set a label but weren't previously a reviewer on this CL).
.Response
----
@@ -3439,11 +3445,8 @@ describes the applied labels.
}
----
A review cannot be set on a change edit. Trying to post a review for a
change edit fails with `409 Conflict`.
It is also possible to add one or more reviewers to a change simultaneously
with a review.
It is also possible to add one or more reviewers or CCs
to a change simultaneously with a review.
.Request
----
@@ -3451,16 +3454,17 @@ with a review.
Content-Type: application/json; charset=UTF-8
{
"message": "Looks good to me, but Jane and John should also take a look.",
"labels": {
"Code-Review": 1
},
"message": "I don't have context here. Jane and maybe John and the project leads should take a look.",
"reviewers": [
{
"reviewer": "jane.roe@example.com"
},
{
"reviewer": "john.doe@example.com"
"reviewer": "john.doe@example.com",
"state": "CC"
}
{
"reviewer": "MyProjectVerifiers",
}
]
}
@@ -3468,8 +3472,8 @@ with a review.
Each element of the `reviewers` list is an instance of
link:#reviewer-input[ReviewerInput]. The corresponding result of
adding each reviewer will be returned in a list of
link:#add-reviewer-result[AddReviewerResult].
adding each reviewer will be returned in a map of inputs to
link:#add-reviewer-result[AddReviewerResult]s.
.Response
----
@@ -3479,36 +3483,66 @@ link:#add-reviewer-result[AddReviewerResult].
)]}'
{
"labels": {
"Code-Review": 1
},
"reviewers": [
{
"reviewers": {
"jane.roe@example.com": {
"input": "jane.roe@example.com",
"approvals": {
"Verified": " 0",
"Code-Review": " 0"
},
"_account_id": 1000097,
"name": "Jane Roe",
"email": "jane.roe@example.com"
"reviewers": [
{
"_account_id": 1000097,
"name": "Jane Roe",
"email": "jane.roe@example.com"
"approvals": {
"Verified": " 0",
"Code-Review": " 0"
},
},
]
},
{
"john.doe@example.com": {
"input": "john.doe@example.com",
"approvals": {
"Verified": " 0",
"Code-Review": " 0"
},
"_account_id": 1000096,
"name": "John Doe",
"email": "john.doe@example.com"
"ccs": [
{
"_account_id": 1000096,
"name": "John Doe",
"email": "john.doe@example.com"
"approvals": {
"Verified": " 0",
"Code-Review": " 0"
},
}
]
},
"MyProjectVerifiers": {
"input": "MyProjectVerifiers",
"reviewers": [
{
"_account_id": 1000098,
"name": "Alice Ansel",
"email": "alice.ansel@example.com"
"approvals": {
"Verified": " 0",
"Code-Review": " 0"
},
},
{
"_account_id": 1000099,
"name": "Bob Bollard",
"email": "bob.bollard@example.com"
"approvals": {
"Verified": " 0",
"Code-Review": " 0"
},
},
]
}
]
}
}
----
If there are any errors returned for reviewers, the entire review request will
be rejected with `400 Bad Request`.
be rejected with `400 Bad Request`. None of the entries will have the
`reviewers` or `ccs` field set, and those which specifically failed will have
the `errors` field set containing details of why they failed.
.Error Response
----
@@ -3519,6 +3553,13 @@ be rejected with `400 Bad Request`.
)]}'
{
"reviewers": {
"jane.roe@example.com": {
"input": "jane.roe@example.com",
"error": "Account of jane.roe@example.com is inactive."
},
"john.doe@example.com": {
"input": "john.doe@example.com"
},
"MyProjectVerifiers": {
"input": "MyProjectVerifiers",
"error": "The group My Group has 15 members. Do you want to add them all as reviewers?",

View File

@@ -54,6 +54,7 @@ import com.google.gerrit.common.TimeUtil;
import com.google.gerrit.common.data.LabelType;
import com.google.gerrit.common.data.Permission;
import com.google.gerrit.extensions.api.changes.AddReviewerInput;
import com.google.gerrit.extensions.api.changes.AddReviewerResult;
import com.google.gerrit.extensions.api.changes.DeleteReviewerInput;
import com.google.gerrit.extensions.api.changes.DeleteVoteInput;
import com.google.gerrit.extensions.api.changes.NotifyHandling;
@@ -90,7 +91,6 @@ 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.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.reviewdb.client.Branch;
@@ -1057,23 +1057,27 @@ public class ChangeIT extends AbstractDaemonTest {
setApiUser(admin);
AddReviewerInput in = new AddReviewerInput();
in.reviewer = user.email;
exception.expect(UnprocessableEntityException.class);
exception.expectMessage("Change not visible to " + user.email);
gApi.changes().id(result.getChangeId()).addReviewer(in);
AddReviewerResult r = gApi.changes().id(result.getChangeId()).addReviewer(in);
assertThat(r.input).isEqualTo(user.email);
assertThat(r.error).contains("does not have permission to see this change");
assertThat(r.reviewers).isNull();
}
@Test
public void addReviewerThatIsInactive() throws Exception {
PushOneCommit.Result r = createChange();
PushOneCommit.Result result = createChange();
String username = name("new-user");
gApi.accounts().create(username).setActive(false);
AddReviewerInput in = new AddReviewerInput();
in.reviewer = username;
exception.expect(UnprocessableEntityException.class);
exception.expectMessage("Account of " + username + " is inactive.");
gApi.changes().id(r.getChangeId()).addReviewer(in);
AddReviewerResult r = gApi.changes().id(result.getChangeId()).addReviewer(in);
assertThat(r.input).isEqualTo(username);
assertThat(r.error).contains("identifies an inactive account");
assertThat(r.reviewers).isNull();
}
@Test

View File

@@ -24,6 +24,7 @@ import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.NoHttpd;
import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.extensions.api.changes.AddReviewerInput;
import com.google.gerrit.extensions.api.changes.AddReviewerResult;
import com.google.gerrit.extensions.api.changes.ReviewInput;
import com.google.gerrit.extensions.api.projects.ConfigInput;
import com.google.gerrit.extensions.client.InheritableBoolean;
@@ -31,8 +32,6 @@ import com.google.gerrit.extensions.client.ListChangesOption;
import com.google.gerrit.extensions.client.ReviewerState;
import com.google.gerrit.extensions.common.AccountInfo;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
import com.google.gerrit.server.mail.Address;
import com.google.gerrit.testutil.FakeEmailSender.Message;
import java.util.EnumSet;
@@ -271,9 +270,9 @@ public class ChangeReviewersByEmailIT extends AbstractDaemonTest {
assume().that(notesMigration.readChanges()).isTrue();
PushOneCommit.Result r = createChange();
exception.expect(UnprocessableEntityException.class);
exception.expectMessage("email invalid");
gApi.changes().id(r.getChangeId()).addReviewer("");
AddReviewerResult result = gApi.changes().id(r.getChangeId()).addReviewer("");
assertThat(result.error).isEqualTo(" is not a valid user identifier");
assertThat(result.reviewers).isNull();
}
@Test
@@ -281,9 +280,9 @@ public class ChangeReviewersByEmailIT extends AbstractDaemonTest {
assume().that(notesMigration.readChanges()).isTrue();
PushOneCommit.Result r = createChange();
exception.expect(UnprocessableEntityException.class);
exception.expectMessage("email invalid");
gApi.changes().id(r.getChangeId()).addReviewer("Foo Bar <foo.bar@");
AddReviewerResult result = gApi.changes().id(r.getChangeId()).addReviewer("Foo Bar <foo.bar@");
assertThat(result.error).isEqualTo("Foo Bar <foo.bar@ is not a valid user identifier");
assertThat(result.reviewers).isNull();
}
@Test
@@ -291,9 +290,12 @@ public class ChangeReviewersByEmailIT extends AbstractDaemonTest {
assume().that(notesMigration.readChanges()).isTrue();
PushOneCommit.Result r = createDraftChange();
exception.expect(BadRequestException.class);
exception.expectMessage("change is not publicly visible");
gApi.changes().id(r.getChangeId()).addReviewer("Foo Bar <foo.bar@gerritcodereview.com>");
AddReviewerResult result =
gApi.changes().id(r.getChangeId()).addReviewer("Foo Bar <foo.bar@gerritcodereview.com>");
assertThat(result.error)
.isEqualTo(
"Foo Bar <foo.bar@gerritcodereview.com> does not have permission to see this change");
assertThat(result.reviewers).isNull();
}
@Test
@@ -306,10 +308,12 @@ public class ChangeReviewersByEmailIT extends AbstractDaemonTest {
PushOneCommit.Result r = createChange();
exception.expect(UnprocessableEntityException.class);
exception.expectMessage(
"Foo Bar <foo.bar@gerritcodereview.com> does not identify a registered user or group");
gApi.changes().id(r.getChangeId()).addReviewer("Foo Bar <foo.bar@gerritcodereview.com>");
AddReviewerResult result =
gApi.changes().id(r.getChangeId()).addReviewer("Foo Bar <foo.bar@gerritcodereview.com>");
assertThat(result.error)
.isEqualTo(
"Foo Bar <foo.bar@gerritcodereview.com> does not identify a registered user or group");
assertThat(result.reviewers).isNull();
}
@Test

View File

@@ -131,9 +131,9 @@ public interface ChangeApi {
IncludedInInfo includedIn() throws RestApiException;
void addReviewer(AddReviewerInput in) throws RestApiException;
AddReviewerResult addReviewer(AddReviewerInput in) throws RestApiException;
void addReviewer(String in) throws RestApiException;
AddReviewerResult addReviewer(String in) throws RestApiException;
SuggestedReviewersRequest suggestReviewers() throws RestApiException;
@@ -359,12 +359,12 @@ public interface ChangeApi {
}
@Override
public void addReviewer(AddReviewerInput in) {
public AddReviewerResult addReviewer(AddReviewerInput in) {
throw new NotImplementedException();
}
@Override
public void addReviewer(String in) {
public AddReviewerResult addReviewer(String in) {
throw new NotImplementedException();
}

View File

@@ -17,6 +17,7 @@ package com.google.gerrit.server.api.changes;
import com.google.gerrit.common.errors.EmailException;
import com.google.gerrit.extensions.api.changes.AbandonInput;
import com.google.gerrit.extensions.api.changes.AddReviewerInput;
import com.google.gerrit.extensions.api.changes.AddReviewerResult;
import com.google.gerrit.extensions.api.changes.AssigneeInput;
import com.google.gerrit.extensions.api.changes.ChangeApi;
import com.google.gerrit.extensions.api.changes.ChangeEditApi;
@@ -406,16 +407,16 @@ class ChangeApiImpl implements ChangeApi {
}
@Override
public void addReviewer(String reviewer) throws RestApiException {
public AddReviewerResult addReviewer(String reviewer) throws RestApiException {
AddReviewerInput in = new AddReviewerInput();
in.reviewer = reviewer;
addReviewer(in);
return addReviewer(in);
}
@Override
public void addReviewer(AddReviewerInput in) throws RestApiException {
public AddReviewerResult addReviewer(AddReviewerInput in) throws RestApiException {
try {
postReviewers.apply(change, in);
return postReviewers.apply(change, in);
} catch (OrmException | IOException | UpdateException | PermissionBackendException e) {
throw new RestApiException("Cannot add change reviewer", e);
}

View File

@@ -23,6 +23,10 @@ public class ChangeMessages extends TranslationBundle {
}
public String revertChangeDefaultMessage;
public String reviewerCantSeeChange;
public String reviewerInactive;
public String reviewerInvalid;
public String reviewerNotFoundUser;
public String reviewerNotFoundUserOrGroup;

View File

@@ -168,20 +168,33 @@ public class PostReviewers implements RestModifyView<ChangeResource, AddReviewer
public Addition prepareApplication(
ChangeResource rsrc, AddReviewerInput input, boolean allowGroup)
throws OrmException, RestApiException, IOException, PermissionBackendException {
throws OrmException, IOException, PermissionBackendException {
String reviewer = input.reviewer;
ReviewerState state = input.state();
NotifyHandling notify = input.notify;
ListMultimap<RecipientType, Account.Id> accountsToNotify = null;
try {
accountsToNotify = notifyUtil.resolveAccounts(input.notifyDetails);
} catch (BadRequestException e) {
return fail(reviewer, e.getMessage());
}
boolean confirmed = input.confirmed();
boolean allowByEmail = projectCache.checkedGet(rsrc.getProject()).isEnableReviewerByEmail();
Addition byAccountId = addByAccountId(rsrc, input, allowGroup, allowByEmail);
Addition byAccountId =
addByAccountId(reviewer, rsrc, state, notify, accountsToNotify, allowGroup, allowByEmail);
if (byAccountId != null) {
return byAccountId;
}
Addition wholeGroup = addWholeGroup(rsrc, input, allowGroup, allowByEmail);
Addition wholeGroup =
addWholeGroup(
reviewer, rsrc, state, notify, accountsToNotify, confirmed, allowGroup, allowByEmail);
if (wholeGroup != null) {
return wholeGroup;
}
return addByEmail(rsrc, input);
return addByEmail(reviewer, rsrc, state, notify, accountsToNotify);
}
Addition ccCurrentUser(CurrentUser user, RevisionResource revision) {
@@ -197,117 +210,72 @@ public class PostReviewers implements RestModifyView<ChangeResource, AddReviewer
@Nullable
private Addition addByAccountId(
ChangeResource rsrc, AddReviewerInput input, boolean allowGroup, boolean allowByEmail)
throws OrmException, RestApiException, PermissionBackendException {
Account.Id accountId = null;
try {
accountId = accounts.parse(input.reviewer).getAccountId();
} catch (UnprocessableEntityException e) {
if (!allowGroup && !allowByEmail) {
throw new UnprocessableEntityException(
MessageFormat.format(ChangeMessages.get().reviewerNotFoundUser, input.reviewer));
}
}
if (accountId != null) {
return putAccount(
input.reviewer,
reviewerFactory.create(rsrc, accountId),
input.state(),
input.notify,
notifyUtil.resolveAccounts(input.notifyDetails));
}
return null;
}
@Nullable
private Addition addWholeGroup(
ChangeResource rsrc, AddReviewerInput input, boolean allowGroup, boolean allowByEmail)
throws OrmException, RestApiException, IOException, PermissionBackendException {
if (!allowGroup) {
return null;
}
try {
return putGroup(rsrc, input);
} catch (UnprocessableEntityException e) {
if (!allowByEmail) {
throw new UnprocessableEntityException(
MessageFormat.format(ChangeMessages.get().reviewerNotFoundUserOrGroup, input.reviewer));
}
}
return null;
}
@Nullable
private Addition addByEmail(ChangeResource rsrc, AddReviewerInput input)
throws OrmException, RestApiException {
return putAccountByEmail(
input.reviewer,
rsrc,
input.state(),
input.notify,
notifyUtil.resolveAccounts(input.notifyDetails));
}
private Addition putAccount(
String reviewer,
ReviewerResource rsrc,
ReviewerState state,
NotifyHandling notify,
ListMultimap<RecipientType, Account.Id> accountsToNotify)
throws UnprocessableEntityException, PermissionBackendException {
Account member = rsrc.getReviewerUser().getAccount();
PermissionBackend.ForRef perm =
permissionBackend.user(rsrc.getReviewerUser()).ref(rsrc.getChange().getDest());
if (isValidReviewer(member, perm)) {
return new Addition(
reviewer,
rsrc.getChangeResource(),
ImmutableSet.of(member.getId()),
null,
state,
notify,
accountsToNotify);
}
if (member.isActive()) {
throw new UnprocessableEntityException(String.format("Change not visible to %s", reviewer));
}
throw new UnprocessableEntityException(String.format("Account of %s is inactive.", reviewer));
}
private Addition putAccountByEmail(
String reviewer,
ChangeResource rsrc,
ReviewerState state,
NotifyHandling notify,
ListMultimap<RecipientType, Account.Id> accountsToNotify)
throws UnprocessableEntityException, OrmException, BadRequestException {
if (!rsrc.getControl().forUser(anonymousProvider.get()).isVisible(dbProvider.get())) {
throw new BadRequestException("change is not publicly visible");
}
if (!migration.readChanges()) {
throw new BadRequestException("feature only supported in NoteDb");
}
Address adr;
ListMultimap<RecipientType, Account.Id> accountsToNotify,
boolean allowGroup,
boolean allowByEmail)
throws OrmException, PermissionBackendException {
Account.Id accountId = null;
try {
adr = Address.parse(reviewer);
} catch (IllegalArgumentException e) {
throw new UnprocessableEntityException(String.format("email invalid %s", reviewer));
accountId = accounts.parse(reviewer).getAccountId();
} catch (UnprocessableEntityException | AuthException e) {
// AuthException won't occur since the user is authenticated at this point.
if (!allowGroup && !allowByEmail) {
// Only return failure if we aren't going to try other interpretations.
return fail(
reviewer, MessageFormat.format(ChangeMessages.get().reviewerNotFoundUser, reviewer));
}
return null;
}
if (!validator.isValid(adr.getEmail())) {
throw new UnprocessableEntityException(String.format("email invalid %s", reviewer));
ReviewerResource rrsrc = reviewerFactory.create(rsrc, accountId);
Account member = rrsrc.getReviewerUser().getAccount();
PermissionBackend.ForRef perm =
permissionBackend.user(rrsrc.getReviewerUser()).ref(rrsrc.getChange().getDest());
if (isValidReviewer(member, perm)) {
return new Addition(
reviewer, rsrc, ImmutableSet.of(member.getId()), null, state, notify, accountsToNotify);
}
return new Addition(
reviewer, rsrc, null, ImmutableList.of(adr), state, notify, accountsToNotify);
if (!member.isActive()) {
return fail(reviewer, MessageFormat.format(ChangeMessages.get().reviewerInactive, reviewer));
}
return fail(
reviewer, MessageFormat.format(ChangeMessages.get().reviewerCantSeeChange, reviewer));
}
private Addition putGroup(ChangeResource rsrc, AddReviewerInput input)
throws RestApiException, OrmException, IOException, PermissionBackendException {
GroupDescription.Basic group = groupsCollection.parseInternal(input.reviewer);
@Nullable
private Addition addWholeGroup(
String reviewer,
ChangeResource rsrc,
ReviewerState state,
NotifyHandling notify,
ListMultimap<RecipientType, Account.Id> accountsToNotify,
boolean confirmed,
boolean allowGroup,
boolean allowByEmail)
throws OrmException, IOException, PermissionBackendException {
if (!allowGroup) {
return null;
}
GroupDescription.Basic group = null;
try {
group = groupsCollection.parseInternal(reviewer);
} catch (UnprocessableEntityException e) {
if (!allowByEmail) {
return fail(
reviewer,
MessageFormat.format(ChangeMessages.get().reviewerNotFoundUserOrGroup, reviewer));
}
return null;
}
if (!isLegalReviewerGroup(group.getGroupUUID())) {
return fail(
input.reviewer,
MessageFormat.format(ChangeMessages.get().groupIsNotAllowed, group.getName()));
reviewer, MessageFormat.format(ChangeMessages.get().groupIsNotAllowed, group.getName()));
}
Set<Account.Id> reviewers = new HashSet<>();
@@ -319,9 +287,11 @@ public class PostReviewers implements RestModifyView<ChangeResource, AddReviewer
.create(control.getUser())
.listAccounts(group.getGroupUUID(), control.getProject().getNameKey());
} catch (NoSuchGroupException e) {
throw new UnprocessableEntityException(e.getMessage());
return fail(
reviewer,
MessageFormat.format(ChangeMessages.get().reviewerNotFoundUserOrGroup, group.getName()));
} catch (NoSuchProjectException e) {
throw new BadRequestException(e.getMessage());
return fail(reviewer, e.getMessage());
}
// if maxAllowed is set to 0, it is allowed to add any number of
@@ -329,18 +299,16 @@ public class PostReviewers implements RestModifyView<ChangeResource, AddReviewer
int maxAllowed = cfg.getInt("addreviewer", "maxAllowed", DEFAULT_MAX_REVIEWERS);
if (maxAllowed > 0 && members.size() > maxAllowed) {
return fail(
input.reviewer,
reviewer,
MessageFormat.format(ChangeMessages.get().groupHasTooManyMembers, group.getName()));
}
// if maxWithoutCheck is set to 0, we never ask for confirmation
int maxWithoutConfirmation =
cfg.getInt("addreviewer", "maxWithoutConfirmation", DEFAULT_MAX_REVIEWERS_WITHOUT_CHECK);
if (!input.confirmed()
&& maxWithoutConfirmation > 0
&& members.size() > maxWithoutConfirmation) {
if (!confirmed && maxWithoutConfirmation > 0 && members.size() > maxWithoutConfirmation) {
return fail(
input.reviewer,
reviewer,
true,
MessageFormat.format(
ChangeMessages.get().groupManyMembersConfirmation, group.getName(), members.size()));
@@ -354,14 +322,32 @@ public class PostReviewers implements RestModifyView<ChangeResource, AddReviewer
}
}
return new Addition(reviewer, rsrc, reviewers, null, state, notify, accountsToNotify);
}
@Nullable
private Addition addByEmail(
String reviewer,
ChangeResource rsrc,
ReviewerState state,
NotifyHandling notify,
ListMultimap<RecipientType, Account.Id> accountsToNotify)
throws OrmException {
if (!rsrc.getControl().forUser(anonymousProvider.get()).isVisible(dbProvider.get())) {
return fail(
reviewer, MessageFormat.format(ChangeMessages.get().reviewerCantSeeChange, reviewer));
}
if (!migration.readChanges()) {
// addByEmail depends on NoteDb.
return fail(
reviewer, MessageFormat.format(ChangeMessages.get().reviewerNotFoundUser, reviewer));
}
Address adr = Address.tryParse(reviewer);
if (adr == null || !validator.isValid(adr.getEmail())) {
return fail(reviewer, MessageFormat.format(ChangeMessages.get().reviewerInvalid, reviewer));
}
return new Addition(
input.reviewer,
rsrc,
reviewers,
null,
input.state(),
input.notify,
notifyUtil.resolveAccounts(input.notifyDetails));
reviewer, rsrc, null, ImmutableList.of(adr), state, notify, accountsToNotify);
}
private boolean isValidReviewer(Account member, PermissionBackend.ForRef perm)

View File

@@ -108,7 +108,7 @@ public class PutAssignee
}
private Addition addAssigneeAsCC(ChangeResource rsrc, String assignee)
throws OrmException, RestApiException, IOException, PermissionBackendException {
throws OrmException, IOException, PermissionBackendException {
AddReviewerInput reviewerInput = new AddReviewerInput();
reviewerInput.reviewer = assignee;
reviewerInput.state = ReviewerState.CC;

View File

@@ -1,9 +1,13 @@
# Changes to this file should also be made in
# gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeMessages.properties
revertChangeDefaultMessage = Revert \"{0}\"\n\nThis reverts commit {1}.
reviewerCantSeeChange = {0} does not have permission to see this change
reviewerInactive = {0} identifies an inactive account
reviewerInvalid = {0} is not a valid user identifier
reviewerNotFoundUser = {0} does not identify a registered user
reviewerNotFoundUserOrGroup = {0} does not identify a registered user or group
groupIsNotAllowed = The group {0} cannot be added as reviewer.
groupIsNotAllowed = The group {0} cannot be added as reviewer.
groupHasTooManyMembers = The group {0} has too many members to add them all as reviewers.
groupManyMembersConfirmation = The group {0} has {1} members. Do you want to add them all as reviewers?