Avoid passing ChangeResource through PostReviewers

The end goal is to make PostReviewers usable from contexts where a
ChangeResource is not available, such as ReceiveCommits. After some
massaging, it turns out that the ChangeResource was only used for the
following reasons, all of which can be avoided:

* As a holder of change + user, which saves a bit of boilerplate in
  passing around to internal methods. This isn't actually necessary, and
  this refactoring is in general moving away from tying the
  PostReviewersOp functionality to the REST API.
* To use the ChangeNotes within the body of PostReviewersOp; this usage
  was recently removed.

Change-Id: Id9d1ed998677f056aaa9f81488b3c12f82e09e6a
This commit is contained in:
Dave Borowitz
2018-10-01 10:53:51 -07:00
parent 9d73b3ebf3
commit e2aa20cc8b
3 changed files with 65 additions and 38 deletions

View File

@@ -15,6 +15,7 @@
package com.google.gerrit.server.restapi.change;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
import static com.google.gerrit.extensions.client.ReviewerState.CC;
import static com.google.gerrit.extensions.client.ReviewerState.REVIEWER;
@@ -155,7 +156,7 @@ public class PostReviewers
throw new BadRequestException("missing reviewer field");
}
Addition addition = prepareApplication(rsrc, input, true);
Addition addition = prepareApplication(rsrc.getNotes(), rsrc.getUser(), input, true);
if (addition.op == null) {
return addition.result;
}
@@ -165,14 +166,31 @@ public class PostReviewers
Change.Id id = rsrc.getChange().getId();
bu.addOp(id, addition.op);
bu.execute();
addition.gatherResults();
// TODO(dborowitz): Should this be re-read to take updates into account?
addition.gatherResults(rsrc.getNotes());
}
return addition.result;
}
/**
* Prepare application of a single {@link AddReviewerInput}.
*
* @param notes change notes.
* @param user user performing the reviewer addition.
* @param input input describing user or group to add as a reviewer.
* @param allowGroup whether to allow
* @return handle describing the addition operation. If the {@code op} field is present, this
* operation may be added to a {@code BatchUpdate}. Otherwise, the {@code error} field
* contains information about an error that occurred
* @throws OrmException
* @throws IOException
* @throws PermissionBackendException
* @throws ConfigInvalidException
*/
public Addition prepareApplication(
ChangeResource rsrc, AddReviewerInput input, boolean allowGroup)
ChangeNotes notes, CurrentUser user, AddReviewerInput input, boolean allowGroup)
throws OrmException, IOException, PermissionBackendException, ConfigInvalidException {
Branch.NameKey dest = notes.getChange().getDest();
String reviewer = input.reviewer;
ReviewerState state = input.state();
NotifyHandling notify = input.notify;
@@ -185,17 +203,26 @@ public class PostReviewers
boolean confirmed = input.confirmed();
boolean allowByEmail =
projectCache
.checkedGet(rsrc.getProject())
.checkedGet(dest.getParentKey())
.is(BooleanProjectConfig.ENABLE_REVIEWER_BY_EMAIL);
Addition byAccountId =
addByAccountId(reviewer, rsrc, state, notify, accountsToNotify, allowGroup, allowByEmail);
addByAccountId(
reviewer, dest, user, state, notify, accountsToNotify, allowGroup, allowByEmail);
Addition wholeGroup = null;
if (byAccountId == null || !byAccountId.exactMatchFound) {
wholeGroup =
addWholeGroup(
reviewer, rsrc, state, notify, accountsToNotify, confirmed, allowGroup, allowByEmail);
reviewer,
dest,
user,
state,
notify,
accountsToNotify,
confirmed,
allowGroup,
allowByEmail);
if (wholeGroup != null && wholeGroup.exactMatchFound) {
return wholeGroup;
}
@@ -208,13 +235,13 @@ public class PostReviewers
return wholeGroup;
}
return addByEmail(reviewer, rsrc, state, notify, accountsToNotify);
return addByEmail(reviewer, notes, user, state, notify, accountsToNotify);
}
Addition ccCurrentUser(CurrentUser user, RevisionResource revision) {
return new Addition(
user.getUserName().orElse(null),
revision.getChangeResource(),
revision.getUser(),
ImmutableSet.of(user.getAccountId()),
null,
CC,
@@ -226,7 +253,8 @@ public class PostReviewers
@Nullable
private Addition addByAccountId(
String reviewer,
ChangeResource rsrc,
Branch.NameKey dest,
CurrentUser user,
ReviewerState state,
NotifyHandling notify,
ListMultimap<RecipientType, Account.Id> accountsToNotify,
@@ -251,10 +279,10 @@ public class PostReviewers
return null;
}
if (isValidReviewer(rsrc.getChange().getDest(), reviewerUser.getAccount())) {
if (isValidReviewer(dest, reviewerUser.getAccount())) {
return new Addition(
reviewer,
rsrc,
user,
ImmutableSet.of(reviewerUser.getAccountId()),
null,
state,
@@ -275,7 +303,8 @@ public class PostReviewers
@Nullable
private Addition addWholeGroup(
String reviewer,
ChangeResource rsrc,
Branch.NameKey dest,
CurrentUser user,
ReviewerState state,
NotifyHandling notify,
ListMultimap<RecipientType, Account.Id> accountsToNotify,
@@ -307,7 +336,7 @@ public class PostReviewers
Set<Account.Id> reviewers = new HashSet<>();
Set<Account> members;
try {
members = groupMembers.listAccounts(group.getGroupUUID(), rsrc.getProject());
members = groupMembers.listAccounts(group.getGroupUUID(), dest.getParentKey());
} catch (NoSuchProjectException e) {
return fail(reviewer, e.getMessage());
}
@@ -333,18 +362,19 @@ public class PostReviewers
}
for (Account member : members) {
if (isValidReviewer(rsrc.getChange().getDest(), member)) {
if (isValidReviewer(dest, member)) {
reviewers.add(member.getId());
}
}
return new Addition(reviewer, rsrc, reviewers, null, state, notify, accountsToNotify, true);
return new Addition(reviewer, user, reviewers, null, state, notify, accountsToNotify, true);
}
@Nullable
private Addition addByEmail(
String reviewer,
ChangeResource rsrc,
ChangeNotes notes,
CurrentUser user,
ReviewerState state,
NotifyHandling notify,
ListMultimap<RecipientType, Account.Id> accountsToNotify)
@@ -352,8 +382,8 @@ public class PostReviewers
try {
permissionBackend
.user(anonymousProvider.get())
.change(rsrc.getNotes())
.database(dbProvider)
.change(notes)
.check(ChangePermission.READ);
} catch (AuthException e) {
return fail(
@@ -370,7 +400,7 @@ public class PostReviewers
return fail(reviewer, MessageFormat.format(ChangeMessages.get().reviewerInvalid, reviewer));
}
return new Addition(
reviewer, rsrc, null, ImmutableList.of(adr), state, notify, accountsToNotify, true);
reviewer, user, null, ImmutableList.of(adr), state, notify, accountsToNotify, true);
}
private boolean isValidReviewer(Branch.NameKey branch, Account member)
@@ -379,9 +409,10 @@ public class PostReviewers
return false;
}
// Does not account for draft status as a user might want to let a
// reviewer see a draft.
try {
// Check ref permission instead of change permission, since change permissions take into
// account the private bit, whereas adding a user as a reviewer is explicitly allowing them to
// see private changes.
permissionBackend
.absentUser(member.getId())
.database(dbProvider)
@@ -405,13 +436,12 @@ public class PostReviewers
}
public class Addition {
final AddReviewerResult result;
final PostReviewersOp op;
public final AddReviewerResult result;
@Nullable public final PostReviewersOp op;
final Set<Account.Id> reviewers;
final Collection<Address> reviewersByEmail;
final ReviewerState state;
final ChangeNotes notes;
final IdentifiedUser caller;
@Nullable final IdentifiedUser caller;
final boolean exactMatchFound;
Addition(String reviewer) {
@@ -420,14 +450,13 @@ public class PostReviewers
reviewers = ImmutableSet.of();
reviewersByEmail = ImmutableSet.of();
state = REVIEWER;
notes = null;
caller = null;
exactMatchFound = false;
}
protected Addition(
Addition(
String reviewer,
ChangeResource rsrc,
CurrentUser caller,
@Nullable Set<Account.Id> reviewers,
@Nullable Collection<Address> reviewersByEmail,
ReviewerState state,
@@ -442,20 +471,16 @@ public class PostReviewers
this.reviewers = reviewers == null ? ImmutableSet.of() : reviewers;
this.reviewersByEmail = reviewersByEmail == null ? ImmutableList.of() : reviewersByEmail;
this.state = state;
notes = rsrc.getNotes();
caller = rsrc.getUser().asIdentifiedUser();
this.caller = caller.asIdentifiedUser();
op =
postReviewersOpFactory.create(
this.reviewers, this.reviewersByEmail, state, notify, accountsToNotify);
this.exactMatchFound = exactMatchFound;
}
void gatherResults() throws OrmException, PermissionBackendException {
if (notes == null || caller == null) {
// When notes or caller is missing this is likely just carrying an error message
// in the contained AddReviewerResult.
return;
}
void gatherResults(ChangeNotes notes) throws OrmException, PermissionBackendException {
checkState(op != null, "addition did not result in an update op");
checkState(op.getResult() != null, "op did not return a result");
ChangeData cd = changeDataFactory.create(dbProvider.get(), notes);
// Generate result details and fill AccountLoader. This occurs outside