Pass notification settings through BatchUpdate's Context
Previously, any notification settings that a BatchUpdateOp wanted to respect in its postUpdate method needed to be resolved from the corresponding REST API input and plumbed through the Op constructor so they could be looked up as needed. Explicit can be good, but this verged into the territory of _too_ explicit. Instead, treat the notification settings as a property of the BatchUpdate. This is somewhat magical, but in this case the magic is justified. Conceptually, associating notification settings with the BatchUpdate makes sense: there is a single set of notification settings that is parsed at the top level from the input, and those settings should apply to *all* emails sent as a result of that BatchUpdate, regardless of the particulars of where they're sent from. Putting them in the Context allows us to treat them as a per-BatchUpdate singleton. We retain the ability to override the NotifyHandling enum on a per-*change* basis, rather than a per-*op* basis, to account for the reduced notifications from WIP changes. Without this change, there's always the possibility of having multiple Ops in the same BatchUpdate that send different emails with different notification settings. For example, we had special code in PostReview to ignore the notification settings embedded in the constituent AddReviewerInputs of a ReviewInput. After this change, we still ignore those settings (as the REST API docs specify), but we don't have to do anything special to do so: we just set the NotifyResolver.Result on the BatchUpdate to the one from the ReviewInput, completely ignoring the AddReviewerInputs. This change also has the effect of removing any logic around notification settings from ReviewerAdder; all the notification logic is in the caller. We still retain a separate mechanism for suppressing all emails from ReviewerAdder, so that PostReview can send only a single email. However, this is stored as a simple boolean, rather than passing around a whole NotifyResolver.Result. It's still not completely ideal, but considering everything else ReviewerAdder has to deal with, it's a small win. Plus, simplifying ReviewerAdder will pave the way for rewriting it in the near future. One downside here is that any check to set NotifyHandling based on properties of the change (e.g. the WIP bit) is racy, since the change was read outside of the BatchUpdate. The risk and consequences of such a race are low enough that the benefits described above still outweigh it. (And it's not like it's the only such race, even though we do try to keep them to a minimum.) This change should have minimal behavior changes. One exception is that moving around calls to NotifyResolver#resolve from the body of a try block to the top level of a REST API handler might result in a 400/422 for invalid input in notify_details rather than logging and silently not sending an email. Some endpoints already had this behavior, so making the failure explicit and consistent is considered a feature. Change-Id: If6f8a44f382f57b9cb10490f74c2b144e904ece8
This commit is contained in:
@@ -45,7 +45,6 @@ public class AbandonOp implements BatchUpdateOp {
|
||||
private final ChangeAbandoned changeAbandoned;
|
||||
|
||||
private final String msgTxt;
|
||||
private final NotifyResolver.Result notify;
|
||||
private final AccountState accountState;
|
||||
|
||||
private Change change;
|
||||
@@ -54,9 +53,7 @@ public class AbandonOp implements BatchUpdateOp {
|
||||
|
||||
public interface Factory {
|
||||
AbandonOp create(
|
||||
@Assisted @Nullable AccountState accountState,
|
||||
@Assisted @Nullable String msgTxt,
|
||||
@Assisted NotifyResolver.Result notify);
|
||||
@Assisted @Nullable AccountState accountState, @Assisted @Nullable String msgTxt);
|
||||
}
|
||||
|
||||
@Inject
|
||||
@@ -66,8 +63,7 @@ public class AbandonOp implements BatchUpdateOp {
|
||||
PatchSetUtil psUtil,
|
||||
ChangeAbandoned changeAbandoned,
|
||||
@Assisted @Nullable AccountState accountState,
|
||||
@Assisted @Nullable String msgTxt,
|
||||
@Assisted NotifyResolver.Result notify) {
|
||||
@Assisted @Nullable String msgTxt) {
|
||||
this.abandonedSenderFactory = abandonedSenderFactory;
|
||||
this.cmUtil = cmUtil;
|
||||
this.psUtil = psUtil;
|
||||
@@ -75,7 +71,6 @@ public class AbandonOp implements BatchUpdateOp {
|
||||
|
||||
this.accountState = accountState;
|
||||
this.msgTxt = Strings.nullToEmpty(msgTxt);
|
||||
this.notify = notify;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -114,6 +109,7 @@ public class AbandonOp implements BatchUpdateOp {
|
||||
|
||||
@Override
|
||||
public void postUpdate(Context ctx) throws OrmException {
|
||||
NotifyResolver.Result notify = ctx.getNotify(change.getId());
|
||||
try {
|
||||
ReplyToChangeSender cm = abandonedSenderFactory.create(ctx.getProject(), change.getId());
|
||||
if (accountState != null) {
|
||||
|
||||
@@ -65,14 +65,10 @@ public class AddReviewersOp implements BatchUpdateOp {
|
||||
* @param accountIds account IDs to add.
|
||||
* @param addresses email addresses to add.
|
||||
* @param state resulting reviewer state.
|
||||
* @param notify notification handling.
|
||||
* @return batch update operation.
|
||||
*/
|
||||
AddReviewersOp create(
|
||||
Set<Account.Id> accountIds,
|
||||
Collection<Address> addresses,
|
||||
ReviewerState state,
|
||||
NotifyResolver.Result notify);
|
||||
Set<Account.Id> accountIds, Collection<Address> addresses, ReviewerState state);
|
||||
}
|
||||
|
||||
@AutoValue
|
||||
@@ -112,7 +108,6 @@ public class AddReviewersOp implements BatchUpdateOp {
|
||||
private final Set<Account.Id> accountIds;
|
||||
private final Collection<Address> addresses;
|
||||
private final ReviewerState state;
|
||||
private final NotifyResolver.Result notify;
|
||||
|
||||
// Unlike addedCCs, addedReviewers is a PatchSetApproval because the AddReviewerResult returned
|
||||
// via the REST API is supposed to include vote information.
|
||||
@@ -121,6 +116,7 @@ public class AddReviewersOp implements BatchUpdateOp {
|
||||
private Collection<Account.Id> addedCCs = ImmutableList.of();
|
||||
private Collection<Address> addedCCsByEmail = ImmutableList.of();
|
||||
|
||||
private boolean sendEmail = true;
|
||||
private Change change;
|
||||
private PatchSet patchSet;
|
||||
private Result opResult;
|
||||
@@ -135,8 +131,7 @@ public class AddReviewersOp implements BatchUpdateOp {
|
||||
AddReviewersEmail addReviewersEmail,
|
||||
@Assisted Set<Account.Id> accountIds,
|
||||
@Assisted Collection<Address> addresses,
|
||||
@Assisted ReviewerState state,
|
||||
@Assisted NotifyResolver.Result notify) {
|
||||
@Assisted ReviewerState state) {
|
||||
checkArgument(state == REVIEWER || state == CC, "must be %s or %s: %s", REVIEWER, CC, state);
|
||||
this.approvalsUtil = approvalsUtil;
|
||||
this.psUtil = psUtil;
|
||||
@@ -148,7 +143,13 @@ public class AddReviewersOp implements BatchUpdateOp {
|
||||
this.accountIds = accountIds;
|
||||
this.addresses = addresses;
|
||||
this.state = state;
|
||||
this.notify = notify;
|
||||
}
|
||||
|
||||
// TODO(dborowitz): This mutable setter is ugly, but a) it's less ugly than adding boolean args
|
||||
// all the way through the constructor stack, and b) this class is slated to be completely
|
||||
// rewritten.
|
||||
public void suppressEmail() {
|
||||
this.sendEmail = false;
|
||||
}
|
||||
|
||||
void setPatchSet(PatchSet patchSet) {
|
||||
@@ -237,14 +238,16 @@ public class AddReviewersOp implements BatchUpdateOp {
|
||||
.setAddedCCs(addedCCs)
|
||||
.setAddedCCsByEmail(addedCCsByEmail)
|
||||
.build();
|
||||
addReviewersEmail.emailReviewers(
|
||||
ctx.getUser().asIdentifiedUser(),
|
||||
change,
|
||||
Lists.transform(addedReviewers, PatchSetApproval::getAccountId),
|
||||
addedCCs,
|
||||
addedReviewersByEmail,
|
||||
addedCCsByEmail,
|
||||
notify);
|
||||
if (sendEmail) {
|
||||
addReviewersEmail.emailReviewers(
|
||||
ctx.getUser().asIdentifiedUser(),
|
||||
change,
|
||||
Lists.transform(addedReviewers, PatchSetApproval::getAccountId),
|
||||
addedCCs,
|
||||
addedReviewersByEmail,
|
||||
addedCCsByEmail,
|
||||
ctx.getNotify(change.getId()));
|
||||
}
|
||||
if (!addedReviewers.isEmpty()) {
|
||||
List<AccountState> reviewers =
|
||||
addedReviewers
|
||||
|
||||
@@ -56,6 +56,7 @@ public class BatchAbandon {
|
||||
}
|
||||
AccountState accountState = user.isIdentifiedUser() ? user.asIdentifiedUser().state() : null;
|
||||
try (BatchUpdate u = updateFactory.create(project, user, TimeUtil.nowTs())) {
|
||||
u.setNotify(notify);
|
||||
for (ChangeData change : changes) {
|
||||
if (!project.equals(change.project())) {
|
||||
throw new ResourceConflictException(
|
||||
@@ -63,7 +64,7 @@ public class BatchAbandon {
|
||||
"Project name \"%s\" doesn't match \"%s\"",
|
||||
change.project().get(), project.get()));
|
||||
}
|
||||
u.addOp(change.getId(), abandonOpFactory.create(accountState, msgTxt, notify));
|
||||
u.addOp(change.getId(), abandonOpFactory.create(accountState, msgTxt));
|
||||
}
|
||||
u.execute();
|
||||
}
|
||||
|
||||
@@ -120,7 +120,6 @@ public class ChangeInserter implements InsertChangeOp {
|
||||
private boolean workInProgress;
|
||||
private List<String> groups = Collections.emptyList();
|
||||
private boolean validate = true;
|
||||
private NotifyResolver.Result notify = NotifyResolver.Result.all();
|
||||
private Map<String, Short> approvals;
|
||||
private RequestScopePropagator requestScopePropagator;
|
||||
private boolean fireRevisionCreated;
|
||||
@@ -251,11 +250,6 @@ public class ChangeInserter implements InsertChangeOp {
|
||||
return this;
|
||||
}
|
||||
|
||||
public ChangeInserter setNotify(NotifyResolver.Result notify) {
|
||||
this.notify = notify;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ChangeInserter setReviewersAndCcs(
|
||||
Iterable<Account.Id> reviewers, Iterable<Account.Id> ccs) {
|
||||
return setReviewersAndCcsAsStrings(
|
||||
@@ -447,6 +441,7 @@ public class ChangeInserter implements InsertChangeOp {
|
||||
@Override
|
||||
public void postUpdate(Context ctx) throws Exception {
|
||||
reviewerAdditions.postUpdate(ctx);
|
||||
NotifyResolver.Result notify = ctx.getNotify(change.getId());
|
||||
if (sendMail && notify.shouldNotify()) {
|
||||
Runnable sender =
|
||||
new Runnable() {
|
||||
|
||||
@@ -531,12 +531,12 @@ public class ConsistencyChecker {
|
||||
}
|
||||
}
|
||||
|
||||
bu.setNotify(NotifyResolver.Result.none());
|
||||
bu.addOp(
|
||||
notes.getChangeId(),
|
||||
inserter
|
||||
.setValidate(false)
|
||||
.setFireRevisionCreated(false)
|
||||
.setNotify(NotifyResolver.Result.none())
|
||||
.setAllowClosed(true)
|
||||
.setMessage("Patch set for merged commit inserted by consistency checker"));
|
||||
bu.addOp(notes.getChangeId(), new FixMergedOp(notFound));
|
||||
|
||||
@@ -63,6 +63,10 @@ public class NotifyResolver {
|
||||
// TODO(dborowitz): Should be ImmutableSetMultimap.
|
||||
public abstract ImmutableListMultimap<RecipientType, Account.Id> accounts();
|
||||
|
||||
public Result withHandling(NotifyHandling notifyHandling) {
|
||||
return create(notifyHandling, accounts());
|
||||
}
|
||||
|
||||
public boolean shouldNotify() {
|
||||
return !accounts().isEmpty() || handling().compareTo(NotifyHandling.NONE) > 0;
|
||||
}
|
||||
|
||||
@@ -91,7 +91,6 @@ public class PatchSetInserter implements BatchUpdateOp {
|
||||
private boolean checkAddPatchSetPermission = true;
|
||||
private List<String> groups = Collections.emptyList();
|
||||
private boolean fireRevisionCreated = true;
|
||||
private NotifyResolver.Result notify = NotifyResolver.Result.all();
|
||||
private boolean allowClosed;
|
||||
|
||||
// Fields set during some phase of BatchUpdate.Op.
|
||||
@@ -165,11 +164,6 @@ public class PatchSetInserter implements BatchUpdateOp {
|
||||
return this;
|
||||
}
|
||||
|
||||
public PatchSetInserter setNotify(NotifyResolver.Result notify) {
|
||||
this.notify = requireNonNull(notify);
|
||||
return this;
|
||||
}
|
||||
|
||||
public PatchSetInserter setAllowClosed(boolean allowClosed) {
|
||||
this.allowClosed = allowClosed;
|
||||
return this;
|
||||
@@ -218,7 +212,7 @@ public class PatchSetInserter implements BatchUpdateOp {
|
||||
psUtil.insert(
|
||||
ctx.getRevWalk(), ctx.getUpdate(psId), psId, commitId, newGroups, null, description);
|
||||
|
||||
if (notify.handling() != NotifyHandling.NONE) {
|
||||
if (ctx.getNotify(change.getId()).handling() != NotifyHandling.NONE) {
|
||||
oldReviewers = approvalsUtil.getReviewers(ctx.getNotes());
|
||||
}
|
||||
|
||||
@@ -247,6 +241,7 @@ public class PatchSetInserter implements BatchUpdateOp {
|
||||
|
||||
@Override
|
||||
public void postUpdate(Context ctx) throws OrmException {
|
||||
NotifyResolver.Result notify = ctx.getNotify(change.getId());
|
||||
if (notify.shouldNotify()) {
|
||||
try {
|
||||
ReplacePatchSetSender cm = replacePatchSetFactory.create(ctx.getProject(), change.getId());
|
||||
|
||||
@@ -182,7 +182,6 @@ public class RebaseChangeOp implements BatchUpdateOp {
|
||||
patchSetInserterFactory
|
||||
.create(notes, rebasedPatchSetId, rebasedCommit)
|
||||
.setDescription("Rebase")
|
||||
.setNotify(NotifyResolver.Result.none())
|
||||
.setFireRevisionCreated(fireRevisionCreated)
|
||||
.setCheckAddPatchSetPermission(checkAddPatchSetPermission)
|
||||
.setValidate(validate);
|
||||
|
||||
@@ -38,7 +38,6 @@ import com.google.gerrit.extensions.api.changes.ReviewerInfo;
|
||||
import com.google.gerrit.extensions.client.ReviewerState;
|
||||
import com.google.gerrit.extensions.common.AccountInfo;
|
||||
import com.google.gerrit.extensions.restapi.AuthException;
|
||||
import com.google.gerrit.extensions.restapi.BadRequestException;
|
||||
import com.google.gerrit.extensions.restapi.RestApiException;
|
||||
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
|
||||
import com.google.gerrit.mail.Address;
|
||||
@@ -148,7 +147,6 @@ public class ReviewerAdder {
|
||||
private final AccountLoader.Factory accountLoaderFactory;
|
||||
private final Config cfg;
|
||||
private final ReviewerJson json;
|
||||
private final NotifyResolver notifyResolver;
|
||||
private final ProjectCache projectCache;
|
||||
private final Provider<AnonymousUser> anonymousProvider;
|
||||
private final AddReviewersOp.Factory addReviewersOpFactory;
|
||||
@@ -163,7 +161,6 @@ public class ReviewerAdder {
|
||||
AccountLoader.Factory accountLoaderFactory,
|
||||
@GerritServerConfig Config cfg,
|
||||
ReviewerJson json,
|
||||
NotifyResolver notifyResolver,
|
||||
ProjectCache projectCache,
|
||||
Provider<AnonymousUser> anonymousProvider,
|
||||
AddReviewersOp.Factory addReviewersOpFactory,
|
||||
@@ -175,7 +172,6 @@ public class ReviewerAdder {
|
||||
this.accountLoaderFactory = accountLoaderFactory;
|
||||
this.cfg = cfg;
|
||||
this.json = json;
|
||||
this.notifyResolver = notifyResolver;
|
||||
this.projectCache = projectCache;
|
||||
this.anonymousProvider = anonymousProvider;
|
||||
this.addReviewersOpFactory = addReviewersOpFactory;
|
||||
@@ -201,23 +197,17 @@ public class ReviewerAdder {
|
||||
ChangeNotes notes, CurrentUser user, AddReviewerInput input, boolean allowGroup)
|
||||
throws OrmException, IOException, PermissionBackendException, ConfigInvalidException {
|
||||
requireNonNull(input.reviewer);
|
||||
NotifyResolver.Result notify;
|
||||
try {
|
||||
notify = resolveNotify(notes, input);
|
||||
} catch (BadRequestException e) {
|
||||
return fail(input, FailureType.OTHER, e.getMessage());
|
||||
}
|
||||
boolean confirmed = input.confirmed();
|
||||
boolean allowByEmail =
|
||||
projectCache
|
||||
.checkedGet(notes.getProjectName())
|
||||
.is(BooleanProjectConfig.ENABLE_REVIEWER_BY_EMAIL);
|
||||
|
||||
ReviewerAddition byAccountId = addByAccountId(input, notes, user, notify);
|
||||
ReviewerAddition byAccountId = addByAccountId(input, notes, user);
|
||||
|
||||
ReviewerAddition wholeGroup = null;
|
||||
if (!byAccountId.exactMatchFound) {
|
||||
wholeGroup = addWholeGroup(input, notes, user, notify, confirmed, allowGroup, allowByEmail);
|
||||
wholeGroup = addWholeGroup(input, notes, user, confirmed, allowGroup, allowByEmail);
|
||||
if (wholeGroup != null && wholeGroup.exactMatchFound) {
|
||||
return wholeGroup;
|
||||
}
|
||||
@@ -239,17 +229,7 @@ public class ReviewerAdder {
|
||||
return wholeGroup;
|
||||
}
|
||||
|
||||
return addByEmail(input, notes, user, notify);
|
||||
}
|
||||
|
||||
private NotifyResolver.Result resolveNotify(ChangeNotes notes, AddReviewerInput input)
|
||||
throws BadRequestException, OrmException, ConfigInvalidException, IOException {
|
||||
NotifyHandling notifyHandling = input.notify;
|
||||
if (notifyHandling == null) {
|
||||
notifyHandling =
|
||||
notes.getChange().isWorkInProgress() ? NotifyHandling.NONE : NotifyHandling.ALL;
|
||||
}
|
||||
return notifyResolver.resolve(notifyHandling, input.notifyDetails);
|
||||
return addByEmail(input, notes, user);
|
||||
}
|
||||
|
||||
public ReviewerAddition ccCurrentUser(CurrentUser user, RevisionResource revision) {
|
||||
@@ -259,13 +239,12 @@ public class ReviewerAdder {
|
||||
revision.getUser(),
|
||||
ImmutableSet.of(user.getAccountId()),
|
||||
null,
|
||||
NotifyResolver.Result.none(),
|
||||
true);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private ReviewerAddition addByAccountId(
|
||||
AddReviewerInput input, ChangeNotes notes, CurrentUser user, NotifyResolver.Result notify)
|
||||
AddReviewerInput input, ChangeNotes notes, CurrentUser user)
|
||||
throws OrmException, PermissionBackendException, IOException, ConfigInvalidException {
|
||||
IdentifiedUser reviewerUser;
|
||||
boolean exactMatchFound = false;
|
||||
@@ -283,13 +262,7 @@ public class ReviewerAdder {
|
||||
|
||||
if (isValidReviewer(notes.getChange().getDest(), reviewerUser.getAccount())) {
|
||||
return new ReviewerAddition(
|
||||
input,
|
||||
notes,
|
||||
user,
|
||||
ImmutableSet.of(reviewerUser.getAccountId()),
|
||||
null,
|
||||
notify,
|
||||
exactMatchFound);
|
||||
input, notes, user, ImmutableSet.of(reviewerUser.getAccountId()), null, exactMatchFound);
|
||||
}
|
||||
return fail(
|
||||
input,
|
||||
@@ -302,7 +275,6 @@ public class ReviewerAdder {
|
||||
AddReviewerInput input,
|
||||
ChangeNotes notes,
|
||||
CurrentUser user,
|
||||
NotifyResolver.Result notify,
|
||||
boolean confirmed,
|
||||
boolean allowGroup,
|
||||
boolean allowByEmail)
|
||||
@@ -374,12 +346,11 @@ public class ReviewerAdder {
|
||||
}
|
||||
}
|
||||
|
||||
return new ReviewerAddition(input, notes, user, reviewers, null, notify, true);
|
||||
return new ReviewerAddition(input, notes, user, reviewers, null, true);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private ReviewerAddition addByEmail(
|
||||
AddReviewerInput input, ChangeNotes notes, CurrentUser user, NotifyResolver.Result notify)
|
||||
private ReviewerAddition addByEmail(AddReviewerInput input, ChangeNotes notes, CurrentUser user)
|
||||
throws PermissionBackendException {
|
||||
try {
|
||||
permissionBackend.user(anonymousProvider.get()).change(notes).check(ChangePermission.READ);
|
||||
@@ -397,7 +368,7 @@ public class ReviewerAdder {
|
||||
FailureType.NOT_FOUND,
|
||||
MessageFormat.format(ChangeMessages.get().reviewerInvalid, input.reviewer));
|
||||
}
|
||||
return new ReviewerAddition(input, notes, user, null, ImmutableList.of(adr), notify, true);
|
||||
return new ReviewerAddition(input, notes, user, null, ImmutableList.of(adr), true);
|
||||
}
|
||||
|
||||
private boolean isValidReviewer(Branch.NameKey branch, Account member)
|
||||
@@ -452,7 +423,6 @@ public class ReviewerAdder {
|
||||
CurrentUser caller,
|
||||
@Nullable Iterable<Account.Id> reviewers,
|
||||
@Nullable Iterable<Address> reviewersByEmail,
|
||||
NotifyResolver.Result notify,
|
||||
boolean exactMatchFound) {
|
||||
checkArgument(
|
||||
reviewers != null || reviewersByEmail != null,
|
||||
@@ -467,7 +437,7 @@ public class ReviewerAdder {
|
||||
this.reviewersByEmail =
|
||||
reviewersByEmail == null ? ImmutableSet.of() : ImmutableSet.copyOf(reviewersByEmail);
|
||||
this.caller = caller.asIdentifiedUser();
|
||||
op = addReviewersOpFactory.create(this.reviewers, this.reviewersByEmail, state(), notify);
|
||||
op = addReviewersOpFactory.create(this.reviewers, this.reviewersByEmail, state());
|
||||
this.exactMatchFound = exactMatchFound;
|
||||
}
|
||||
|
||||
@@ -557,7 +527,12 @@ public class ReviewerAdder {
|
||||
.collect(toImmutableList());
|
||||
List<ReviewerAddition> additions = new ArrayList<>();
|
||||
for (AddReviewerInput input : sorted) {
|
||||
additions.add(prepare(notes, user, input, allowGroup));
|
||||
ReviewerAddition addition = prepare(notes, user, input, allowGroup);
|
||||
if (addition.op != null) {
|
||||
// Assume any callers preparing a list of batch insertions are handling their own email.
|
||||
addition.op.suppressEmail();
|
||||
}
|
||||
additions.add(addition);
|
||||
}
|
||||
return new ReviewerAdditionList(additions);
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
|
||||
package com.google.gerrit.server.change;
|
||||
|
||||
import com.google.common.base.MoreObjects;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.gerrit.common.Nullable;
|
||||
@@ -91,9 +90,9 @@ public class WorkInProgressOp implements BatchUpdateOp {
|
||||
private final PatchSetUtil psUtil;
|
||||
private final boolean workInProgress;
|
||||
private final Input in;
|
||||
private final NotifyResolver.Result notify;
|
||||
private final WorkInProgressStateChanged stateChanged;
|
||||
|
||||
private boolean sendEmail = true;
|
||||
private Change change;
|
||||
private ChangeNotes notes;
|
||||
private PatchSet ps;
|
||||
@@ -113,10 +112,10 @@ public class WorkInProgressOp implements BatchUpdateOp {
|
||||
this.stateChanged = stateChanged;
|
||||
this.workInProgress = workInProgress;
|
||||
this.in = in;
|
||||
notify =
|
||||
NotifyResolver.Result.create(
|
||||
MoreObjects.firstNonNull(
|
||||
in.notify, workInProgress ? NotifyHandling.NONE : NotifyHandling.ALL));
|
||||
}
|
||||
|
||||
public void suppressEmail() {
|
||||
this.sendEmail = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -160,7 +159,10 @@ public class WorkInProgressOp implements BatchUpdateOp {
|
||||
@Override
|
||||
public void postUpdate(Context ctx) {
|
||||
stateChanged.fire(change, ps, ctx.getAccount(), ctx.getWhen());
|
||||
if (workInProgress || notify.handling().compareTo(NotifyHandling.OWNER_REVIEWERS) < 0) {
|
||||
NotifyResolver.Result notify = ctx.getNotify(change.getId());
|
||||
if (workInProgress
|
||||
|| notify.handling().compareTo(NotifyHandling.OWNER_REVIEWERS) < 0
|
||||
|| !sendEmail) {
|
||||
return;
|
||||
}
|
||||
email
|
||||
|
||||
@@ -169,8 +169,7 @@ public class ChangeEditUtil {
|
||||
|
||||
RevCommit squashed = squashEdit(rw, oi, edit.getEditCommit(), basePatchSet);
|
||||
PatchSet.Id psId = ChangeUtil.nextPatchSetId(repo, change.currentPatchSetId());
|
||||
PatchSetInserter inserter =
|
||||
patchSetInserterFactory.create(notes, psId, squashed).setNotify(notify);
|
||||
PatchSetInserter inserter = patchSetInserterFactory.create(notes, psId, squashed);
|
||||
|
||||
StringBuilder message =
|
||||
new StringBuilder("Patch Set ").append(inserter.getPatchSetId().get()).append(": ");
|
||||
@@ -191,6 +190,7 @@ public class ChangeEditUtil {
|
||||
|
||||
try (BatchUpdate bu = updateFactory.create(change.getProject(), user, TimeUtil.nowTs())) {
|
||||
bu.setRepository(repo, rw, oi);
|
||||
bu.setNotify(notify);
|
||||
bu.addOp(change.getId(), inserter.setMessage(message.toString()));
|
||||
bu.addOp(
|
||||
change.getId(),
|
||||
|
||||
@@ -682,7 +682,7 @@ class ReceiveCommits {
|
||||
// Update superproject gitlinks if required.
|
||||
if (!branches.isEmpty()) {
|
||||
try (MergeOpRepoManager orm = ormProvider.get()) {
|
||||
orm.setContext(TimeUtil.nowTs(), user);
|
||||
orm.setContext(TimeUtil.nowTs(), user, NotifyResolver.Result.none());
|
||||
SubmoduleOp op = subOpFactory.create(branches, orm);
|
||||
op.updateSuperProjects();
|
||||
} catch (SubmoduleException e) {
|
||||
@@ -787,9 +787,15 @@ class ReceiveCommits {
|
||||
RevWalk rw = new RevWalk(reader)) {
|
||||
bu.setRepository(repo, rw, ins);
|
||||
bu.setRefLogMessage("push");
|
||||
if (magicBranch != null) {
|
||||
bu.setNotify(magicBranch.getNotifyForNewChange());
|
||||
}
|
||||
|
||||
logger.atFine().log("Adding %d replace requests", newChanges.size());
|
||||
for (ReplaceRequest replace : replaceByChange.values()) {
|
||||
if (magicBranch != null) {
|
||||
bu.setNotifyHandling(replace.ontoChange, magicBranch.getNotifyHandling(replace.notes));
|
||||
}
|
||||
replace.addOps(bu, replaceProgress);
|
||||
}
|
||||
|
||||
@@ -1578,31 +1584,25 @@ class ReceiveCommits {
|
||||
}
|
||||
|
||||
NotifyResolver.Result getNotifyForNewChange() {
|
||||
return getNotifyImpl(null);
|
||||
}
|
||||
|
||||
NotifyResolver.Result getNotify(ChangeNotes notes) {
|
||||
return getNotifyImpl(requireNonNull(notes));
|
||||
}
|
||||
|
||||
private NotifyResolver.Result getNotifyImpl(@Nullable ChangeNotes notes) {
|
||||
NotifyHandling notifyHandling = this.notifyHandling;
|
||||
if (notifyHandling == null) {
|
||||
if (workInProgress || (notes != null && !ready && notes.getChange().isWorkInProgress())) {
|
||||
notifyHandling = NotifyHandling.OWNER;
|
||||
} else {
|
||||
notifyHandling = NotifyHandling.ALL;
|
||||
}
|
||||
}
|
||||
|
||||
return NotifyResolver.Result.create(
|
||||
notifyHandling,
|
||||
firstNonNull(notifyHandling, workInProgress ? NotifyHandling.OWNER : NotifyHandling.ALL),
|
||||
ImmutableListMultimap.<RecipientType, Account.Id>builder()
|
||||
.putAll(RecipientType.TO, notifyTo)
|
||||
.putAll(RecipientType.CC, notifyCc)
|
||||
.putAll(RecipientType.BCC, notifyBcc)
|
||||
.build());
|
||||
}
|
||||
|
||||
NotifyHandling getNotifyHandling(ChangeNotes notes) {
|
||||
requireNonNull(notes);
|
||||
if (notifyHandling != null) {
|
||||
return notifyHandling;
|
||||
}
|
||||
if (workInProgress || (!ready && notes.getChange().isWorkInProgress())) {
|
||||
return NotifyHandling.OWNER;
|
||||
}
|
||||
return NotifyHandling.ALL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2440,13 +2440,13 @@ class ReceiveCommits {
|
||||
msg.append("\n").append(magicBranch.message);
|
||||
}
|
||||
|
||||
bu.setNotify(magicBranch.getNotifyForNewChange());
|
||||
bu.insertChange(
|
||||
ins.setReviewersAndCcsAsStrings(
|
||||
magicBranch.getCombinedReviewers(fromFooters),
|
||||
magicBranch.getCombinedCcs(fromFooters))
|
||||
.setApprovals(approvals)
|
||||
.setMessage(msg.toString())
|
||||
.setNotify(magicBranch.getNotifyForNewChange())
|
||||
.setRequestScopePropagator(requestScopePropagator)
|
||||
.setSendMail(true)
|
||||
.setPatchSetDescription(magicBranch.message));
|
||||
|
||||
@@ -512,8 +512,7 @@ public class ReplaceOp implements BatchUpdateOp {
|
||||
}
|
||||
}
|
||||
|
||||
NotifyResolver.Result notify =
|
||||
magicBranch != null ? magicBranch.getNotify(notes) : NotifyResolver.Result.all();
|
||||
NotifyResolver.Result notify = ctx.getNotify(notes.getChangeId());
|
||||
if (shouldPublishComments()) {
|
||||
emailCommentsFactory
|
||||
.create(
|
||||
@@ -554,9 +553,7 @@ public class ReplaceOp implements BatchUpdateOp {
|
||||
cm.setFrom(ctx.getAccount().getAccount().getId());
|
||||
cm.setPatchSet(newPatchSet, info);
|
||||
cm.setChangeMessage(msg.getMessage(), ctx.getWhen());
|
||||
if (magicBranch != null) {
|
||||
cm.setNotify(magicBranch.getNotify(notes));
|
||||
}
|
||||
cm.setNotify(ctx.getNotify(notes.getChangeId()));
|
||||
cm.addReviewers(
|
||||
Streams.concat(
|
||||
oldRecipients.getReviewers().stream(),
|
||||
|
||||
@@ -45,7 +45,6 @@ import com.google.gerrit.server.account.AccountCache;
|
||||
import com.google.gerrit.server.account.AccountState;
|
||||
import com.google.gerrit.server.account.Emails;
|
||||
import com.google.gerrit.server.change.EmailReviewComments;
|
||||
import com.google.gerrit.server.change.NotifyResolver;
|
||||
import com.google.gerrit.server.config.UrlFormatter;
|
||||
import com.google.gerrit.server.extensions.events.CommentAdded;
|
||||
import com.google.gerrit.server.mail.MailFilter;
|
||||
@@ -314,7 +313,7 @@ public class MailProcessor {
|
||||
// Send email notifications
|
||||
outgoingMailFactory
|
||||
.create(
|
||||
NotifyResolver.Result.all(),
|
||||
ctx.getNotify(notes.getChangeId()),
|
||||
notes,
|
||||
patchSet,
|
||||
ctx.getUser().asIdentifiedUser(),
|
||||
|
||||
@@ -120,8 +120,9 @@ public class Abandon extends RetryingRestModifyView<ChangeResource, AbandonInput
|
||||
NotifyResolver.Result notify)
|
||||
throws RestApiException, UpdateException {
|
||||
AccountState accountState = user.isIdentifiedUser() ? user.asIdentifiedUser().state() : null;
|
||||
AbandonOp op = abandonOpFactory.create(accountState, msgTxt, notify);
|
||||
AbandonOp op = abandonOpFactory.create(accountState, msgTxt);
|
||||
try (BatchUpdate u = updateFactory.create(notes.getProjectName(), user, TimeUtil.nowTs())) {
|
||||
u.setNotify(notify);
|
||||
u.addOp(notes.getChangeId(), op).execute();
|
||||
}
|
||||
return op.getChange();
|
||||
|
||||
@@ -249,11 +249,12 @@ public class CherryPickChange {
|
||||
}
|
||||
try (BatchUpdate bu = batchUpdateFactory.create(project, identifiedUser, now)) {
|
||||
bu.setRepository(git, revWalk, oi);
|
||||
bu.setNotify(resolveNotify(input));
|
||||
Change.Id changeId;
|
||||
if (destChanges.size() == 1) {
|
||||
// The change key exists on the destination branch. The cherry pick
|
||||
// will be added as a new patch set.
|
||||
changeId = insertPatchSet(bu, git, destChanges.get(0).notes(), cherryPickCommit, input);
|
||||
changeId = insertPatchSet(bu, git, destChanges.get(0).notes(), cherryPickCommit);
|
||||
} else {
|
||||
// Change key not found on destination branch. We can create a new
|
||||
// change.
|
||||
@@ -318,19 +319,12 @@ public class CherryPickChange {
|
||||
}
|
||||
|
||||
private Change.Id insertPatchSet(
|
||||
BatchUpdate bu,
|
||||
Repository git,
|
||||
ChangeNotes destNotes,
|
||||
CodeReviewCommit cherryPickCommit,
|
||||
CherryPickInput input)
|
||||
throws IOException, OrmException, BadRequestException, ConfigInvalidException {
|
||||
BatchUpdate bu, Repository git, ChangeNotes destNotes, CodeReviewCommit cherryPickCommit)
|
||||
throws IOException {
|
||||
Change destChange = destNotes.getChange();
|
||||
PatchSet.Id psId = ChangeUtil.nextPatchSetId(git, destChange.currentPatchSetId());
|
||||
PatchSetInserter inserter = patchSetInserterFactory.create(destNotes, psId, cherryPickCommit);
|
||||
NotifyResolver.Result notify = resolveNotify(input);
|
||||
inserter
|
||||
.setMessage("Uploaded patch set " + inserter.getPatchSetId().get() + ".")
|
||||
.setNotify(notify);
|
||||
inserter.setMessage("Uploaded patch set " + inserter.getPatchSetId().get() + ".");
|
||||
bu.addOp(destChange.getId(), inserter);
|
||||
return destChange.getId();
|
||||
}
|
||||
@@ -343,19 +337,17 @@ public class CherryPickChange {
|
||||
@Nullable Change sourceChange,
|
||||
ObjectId sourceCommit,
|
||||
CherryPickInput input)
|
||||
throws OrmException, IOException, BadRequestException, ConfigInvalidException {
|
||||
throws OrmException, IOException {
|
||||
Change.Id changeId = new Change.Id(seq.nextChangeId());
|
||||
ChangeInserter ins = changeInserterFactory.create(changeId, cherryPickCommit, refName);
|
||||
Branch.NameKey sourceBranch = sourceChange == null ? null : sourceChange.getDest();
|
||||
NotifyResolver.Result notify = resolveNotify(input);
|
||||
ins.setMessage(
|
||||
messageForDestinationChange(
|
||||
ins.getPatchSetId(), sourceBranch, sourceCommit, cherryPickCommit))
|
||||
.setTopic(topic)
|
||||
.setWorkInProgress(
|
||||
(sourceChange != null && sourceChange.isWorkInProgress())
|
||||
|| !cherryPickCommit.getFilesWithGitConflicts().isEmpty())
|
||||
.setNotify(notify);
|
||||
|| !cherryPickCommit.getFilesWithGitConflicts().isEmpty());
|
||||
if (input.keepReviewers && sourceChange != null) {
|
||||
ReviewerSet reviewerSet =
|
||||
approvalsUtil.getReviewers(changeNotesFactory.createChecked(sourceChange));
|
||||
|
||||
@@ -305,12 +305,11 @@ public class CreateChange
|
||||
ins.setPrivate(input.isPrivate);
|
||||
ins.setWorkInProgress(input.workInProgress);
|
||||
ins.setGroups(groups);
|
||||
NotifyResolver.Result notify =
|
||||
notifyResolver.resolve(
|
||||
firstNonNull(input.notify, NotifyHandling.ALL), input.notifyDetails);
|
||||
ins.setNotify(notify);
|
||||
try (BatchUpdate bu = updateFactory.create(projectState.getNameKey(), me, now)) {
|
||||
bu.setRepository(git, rw, oi);
|
||||
bu.setNotify(
|
||||
notifyResolver.resolve(
|
||||
firstNonNull(input.notify, NotifyHandling.ALL), input.notifyDetails));
|
||||
bu.insertChange(ins);
|
||||
bu.execute();
|
||||
}
|
||||
|
||||
@@ -183,9 +183,9 @@ public class CreateMergePatchSet
|
||||
patchSetInserterFactory.create(rsrc.getNotes(), nextPsId, newCommit);
|
||||
try (BatchUpdate bu = updateFactory.create(project, me, now)) {
|
||||
bu.setRepository(git, rw, oi);
|
||||
bu.setNotify(NotifyResolver.Result.none());
|
||||
psInserter
|
||||
.setMessage("Uploaded patch set " + nextPsId.get() + ".")
|
||||
.setNotify(NotifyResolver.Result.none())
|
||||
.setCheckAddPatchSetPermission(false);
|
||||
if (groups != null) {
|
||||
psInserter.setGroups(groups);
|
||||
|
||||
@@ -15,8 +15,11 @@
|
||||
package com.google.gerrit.server.restapi.change;
|
||||
|
||||
import com.google.gerrit.extensions.api.changes.DeleteReviewerInput;
|
||||
import com.google.gerrit.extensions.api.changes.NotifyHandling;
|
||||
import com.google.gerrit.extensions.restapi.Response;
|
||||
import com.google.gerrit.extensions.restapi.RestApiException;
|
||||
import com.google.gerrit.reviewdb.client.Change;
|
||||
import com.google.gerrit.server.change.NotifyResolver;
|
||||
import com.google.gerrit.server.change.ReviewerResource;
|
||||
import com.google.gerrit.server.update.BatchUpdate;
|
||||
import com.google.gerrit.server.update.BatchUpdateOp;
|
||||
@@ -57,6 +60,7 @@ public class DeleteReviewer
|
||||
rsrc.getChangeResource().getProject(),
|
||||
rsrc.getChangeResource().getUser(),
|
||||
TimeUtil.nowTs())) {
|
||||
bu.setNotify(getNotify(rsrc.getChange(), input));
|
||||
BatchUpdateOp op;
|
||||
if (rsrc.isByEmail()) {
|
||||
op = deleteReviewerByEmailOpFactory.create(rsrc.getReviewerByEmail(), input);
|
||||
@@ -68,4 +72,12 @@ public class DeleteReviewer
|
||||
}
|
||||
return Response.none();
|
||||
}
|
||||
|
||||
private static NotifyResolver.Result getNotify(Change change, DeleteReviewerInput input) {
|
||||
NotifyHandling notifyHandling = input.notify;
|
||||
if (notifyHandling == null) {
|
||||
notifyHandling = change.isWorkInProgress() ? NotifyHandling.NONE : NotifyHandling.ALL;
|
||||
}
|
||||
return NotifyResolver.Result.create(notifyHandling);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,11 +14,8 @@
|
||||
|
||||
package com.google.gerrit.server.restapi.change;
|
||||
|
||||
import static com.google.common.base.MoreObjects.firstNonNull;
|
||||
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import com.google.gerrit.extensions.api.changes.DeleteReviewerInput;
|
||||
import com.google.gerrit.extensions.api.changes.NotifyHandling;
|
||||
import com.google.gerrit.mail.Address;
|
||||
import com.google.gerrit.reviewdb.client.Change;
|
||||
import com.google.gerrit.reviewdb.client.ChangeMessage;
|
||||
@@ -42,7 +39,6 @@ public class DeleteReviewerByEmailOp implements BatchUpdateOp {
|
||||
}
|
||||
|
||||
private final DeleteReviewerSender.Factory deleteReviewerSenderFactory;
|
||||
private final NotifyResolver notifyResolver;
|
||||
private final Address reviewer;
|
||||
private final DeleteReviewerInput input;
|
||||
|
||||
@@ -52,11 +48,9 @@ public class DeleteReviewerByEmailOp implements BatchUpdateOp {
|
||||
@Inject
|
||||
DeleteReviewerByEmailOp(
|
||||
DeleteReviewerSender.Factory deleteReviewerSenderFactory,
|
||||
NotifyResolver notifyResolver,
|
||||
@Assisted Address reviewer,
|
||||
@Assisted DeleteReviewerInput input) {
|
||||
this.deleteReviewerSenderFactory = deleteReviewerSenderFactory;
|
||||
this.notifyResolver = notifyResolver;
|
||||
this.reviewer = reviewer;
|
||||
this.input = input;
|
||||
}
|
||||
@@ -81,17 +75,8 @@ public class DeleteReviewerByEmailOp implements BatchUpdateOp {
|
||||
|
||||
@Override
|
||||
public void postUpdate(Context ctx) {
|
||||
if (input.notify == null) {
|
||||
if (change.isWorkInProgress()) {
|
||||
input.notify = NotifyHandling.NONE;
|
||||
} else {
|
||||
input.notify = NotifyHandling.ALL;
|
||||
}
|
||||
}
|
||||
try {
|
||||
NotifyResolver.Result notify =
|
||||
notifyResolver.resolve(
|
||||
firstNonNull(input.notify, NotifyHandling.ALL), input.notifyDetails);
|
||||
NotifyResolver.Result notify = ctx.getNotify(change.getId());
|
||||
if (!notify.shouldNotify()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -69,7 +69,6 @@ public class DeleteReviewerOp implements BatchUpdateOp {
|
||||
private final ReviewerDeleted reviewerDeleted;
|
||||
private final Provider<IdentifiedUser> user;
|
||||
private final DeleteReviewerSender.Factory deleteReviewerSenderFactory;
|
||||
private final NotifyResolver notifyResolver;
|
||||
private final RemoveReviewerControl removeReviewerControl;
|
||||
private final ProjectCache projectCache;
|
||||
|
||||
@@ -91,7 +90,6 @@ public class DeleteReviewerOp implements BatchUpdateOp {
|
||||
ReviewerDeleted reviewerDeleted,
|
||||
Provider<IdentifiedUser> user,
|
||||
DeleteReviewerSender.Factory deleteReviewerSenderFactory,
|
||||
NotifyResolver notifyResolver,
|
||||
RemoveReviewerControl removeReviewerControl,
|
||||
ProjectCache projectCache,
|
||||
@Assisted AccountState reviewerAccount,
|
||||
@@ -103,7 +101,6 @@ public class DeleteReviewerOp implements BatchUpdateOp {
|
||||
this.reviewerDeleted = reviewerDeleted;
|
||||
this.user = user;
|
||||
this.deleteReviewerSenderFactory = deleteReviewerSenderFactory;
|
||||
this.notifyResolver = notifyResolver;
|
||||
this.removeReviewerControl = removeReviewerControl;
|
||||
this.projectCache = projectCache;
|
||||
this.reviewer = reviewerAccount;
|
||||
@@ -170,15 +167,16 @@ public class DeleteReviewerOp implements BatchUpdateOp {
|
||||
|
||||
@Override
|
||||
public void postUpdate(Context ctx) {
|
||||
if (input.notify == null) {
|
||||
if (currChange.isWorkInProgress()) {
|
||||
input.notify = oldApprovals.isEmpty() ? NotifyHandling.NONE : NotifyHandling.OWNER;
|
||||
} else {
|
||||
input.notify = NotifyHandling.ALL;
|
||||
}
|
||||
NotifyResolver.Result notify = ctx.getNotify(currChange.getId());
|
||||
if (input.notify == null
|
||||
&& currChange.isWorkInProgress()
|
||||
&& !oldApprovals.isEmpty()
|
||||
&& notify.handling().compareTo(NotifyHandling.OWNER) < 0) {
|
||||
// Override NotifyHandling from the context to notify owner if votes were removed on a WIP
|
||||
// change.
|
||||
notify = notify.withHandling(NotifyHandling.OWNER);
|
||||
}
|
||||
try {
|
||||
NotifyResolver.Result notify = notifyResolver.resolve(input.notify, input.notifyDetails);
|
||||
if (notify.shouldNotify()) {
|
||||
emailReviewers(ctx.getProject(), currChange, changeMessage, notify);
|
||||
}
|
||||
@@ -193,7 +191,7 @@ public class DeleteReviewerOp implements BatchUpdateOp {
|
||||
changeMessage.getMessage(),
|
||||
newApprovals,
|
||||
oldApprovals,
|
||||
input.notify,
|
||||
notify.handling(),
|
||||
ctx.getWhen());
|
||||
}
|
||||
|
||||
|
||||
@@ -62,6 +62,7 @@ import com.google.inject.Singleton;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||
|
||||
@Singleton
|
||||
public class DeleteVote extends RetryingRestModifyView<VoteResource, DeleteVoteInput, Response<?>> {
|
||||
@@ -104,7 +105,7 @@ public class DeleteVote extends RetryingRestModifyView<VoteResource, DeleteVoteI
|
||||
@Override
|
||||
protected Response<?> applyImpl(
|
||||
BatchUpdate.Factory updateFactory, VoteResource rsrc, DeleteVoteInput input)
|
||||
throws RestApiException, UpdateException, IOException {
|
||||
throws RestApiException, UpdateException, IOException, OrmException, ConfigInvalidException {
|
||||
if (input == null) {
|
||||
input = new DeleteVoteInput();
|
||||
}
|
||||
@@ -124,6 +125,9 @@ public class DeleteVote extends RetryingRestModifyView<VoteResource, DeleteVoteI
|
||||
try (BatchUpdate bu =
|
||||
updateFactory.create(
|
||||
change.getProject(), r.getChangeResource().getUser(), TimeUtil.nowTs())) {
|
||||
bu.setNotify(
|
||||
notifyResolver.resolve(
|
||||
firstNonNull(input.notify, NotifyHandling.ALL), input.notifyDetails));
|
||||
bu.addOp(
|
||||
change.getId(),
|
||||
new Op(
|
||||
@@ -219,9 +223,7 @@ public class DeleteVote extends RetryingRestModifyView<VoteResource, DeleteVoteI
|
||||
|
||||
IdentifiedUser user = ctx.getIdentifiedUser();
|
||||
try {
|
||||
NotifyResolver.Result notify =
|
||||
notifyResolver.resolve(
|
||||
firstNonNull(input.notify, NotifyHandling.ALL), input.notifyDetails);
|
||||
NotifyResolver.Result notify = ctx.getNotify(change.getId());
|
||||
if (notify.shouldNotify()) {
|
||||
ReplyToChangeSender cm = deleteVoteSenderFactory.create(ctx.getProject(), change.getId());
|
||||
cm.setFrom(user.getAccountId());
|
||||
|
||||
@@ -255,8 +255,6 @@ public class PostReview
|
||||
input.notify = defaultNotify(revision.getChange(), input);
|
||||
}
|
||||
|
||||
NotifyResolver.Result notify = notifyResolver.resolve(input.notify, input.notifyDetails);
|
||||
|
||||
Map<String, AddReviewerResult> reviewerJsonResults = null;
|
||||
List<ReviewerAddition> reviewerResults = Lists.newArrayList();
|
||||
boolean hasError = false;
|
||||
@@ -264,12 +262,6 @@ public class PostReview
|
||||
if (input.reviewers != null) {
|
||||
reviewerJsonResults = Maps.newHashMap();
|
||||
for (AddReviewerInput reviewerInput : input.reviewers) {
|
||||
// Prevent individual AddReviewersOps from sending one email each. Instead, we call
|
||||
// batchEmailReviewers at the very end to send out a single email.
|
||||
// TODO(dborowitz): I think this still sends out separate emails if any of input.reviewers
|
||||
// specifies explicit accountsToNotify. Unclear whether that's a good thing.
|
||||
reviewerInput.notify = NotifyHandling.NONE;
|
||||
|
||||
ReviewerAddition result =
|
||||
reviewerAdder.prepare(revision.getNotes(), revision.getUser(), reviewerInput, true);
|
||||
reviewerJsonResults.put(reviewerInput.reviewer, result.result);
|
||||
@@ -312,6 +304,7 @@ public class PostReview
|
||||
// updated set of reviewers. Also keep track of whether the user added
|
||||
// themselves as a reviewer or to the CC list.
|
||||
for (ReviewerAddition reviewerResult : reviewerResults) {
|
||||
reviewerResult.op.suppressEmail(); // Send a single batch email below.
|
||||
bu.addOp(revision.getChange().getId(), reviewerResult.op);
|
||||
if (!ccOrReviewer && reviewerResult.result.reviewers != null) {
|
||||
for (ReviewerInfo reviewerInfo : reviewerResult.result.reviewers) {
|
||||
@@ -336,6 +329,7 @@ public class PostReview
|
||||
// isn't being explicitly added, and isn't voting on any label.
|
||||
// Automatically CC them on this change so they receive replies.
|
||||
ReviewerAddition selfAddition = reviewerAdder.ccCurrentUser(revision.getUser(), revision);
|
||||
selfAddition.op.suppressEmail();
|
||||
bu.addOp(revision.getChange().getId(), selfAddition.op);
|
||||
}
|
||||
|
||||
@@ -353,19 +347,21 @@ public class PostReview
|
||||
output.ready = true;
|
||||
}
|
||||
|
||||
// Suppress notifications in WorkInProgressOp, we'll take care of
|
||||
// them in this endpoint.
|
||||
WorkInProgressOp.Input wipIn = new WorkInProgressOp.Input();
|
||||
wipIn.notify = NotifyHandling.NONE;
|
||||
bu.addOp(
|
||||
revision.getChange().getId(),
|
||||
workInProgressOpFactory.create(input.workInProgress, wipIn));
|
||||
WorkInProgressOp wipOp =
|
||||
workInProgressOpFactory.create(input.workInProgress, new WorkInProgressOp.Input());
|
||||
wipOp.suppressEmail();
|
||||
bu.addOp(revision.getChange().getId(), wipOp);
|
||||
}
|
||||
|
||||
// Add the review op.
|
||||
bu.addOp(
|
||||
revision.getChange().getId(),
|
||||
new Op(projectState, revision.getPatchSet().getId(), input, notify));
|
||||
new Op(projectState, revision.getPatchSet().getId(), input));
|
||||
|
||||
// Notify based on ReviewInput, ignoring the notify settings from any AddReviewerInputs.
|
||||
NotifyResolver.Result notify =
|
||||
notifyResolver.resolve(getNotifyHandling(input, output, revision), input.notifyDetails);
|
||||
bu.setNotify(notify);
|
||||
|
||||
bu.execute();
|
||||
|
||||
@@ -382,6 +378,17 @@ public class PostReview
|
||||
return Response.ok(output);
|
||||
}
|
||||
|
||||
private NotifyHandling getNotifyHandling(
|
||||
ReviewInput input, ReviewResult output, RevisionResource revision) {
|
||||
if (input.notify != null) {
|
||||
return input.notify;
|
||||
}
|
||||
if ((output.ready != null && output.ready) || !revision.getChange().isWorkInProgress()) {
|
||||
return NotifyHandling.ALL;
|
||||
}
|
||||
return NotifyHandling.NONE;
|
||||
}
|
||||
|
||||
private NotifyHandling defaultNotify(Change c, ReviewInput in) {
|
||||
boolean workInProgress = c.isWorkInProgress();
|
||||
if (in.workInProgress) {
|
||||
@@ -828,7 +835,6 @@ public class PostReview
|
||||
private final ProjectState projectState;
|
||||
private final PatchSet.Id psId;
|
||||
private final ReviewInput in;
|
||||
private final NotifyResolver.Result notify;
|
||||
|
||||
private IdentifiedUser user;
|
||||
private ChangeNotes notes;
|
||||
@@ -839,12 +845,10 @@ public class PostReview
|
||||
private Map<String, Short> approvals = new HashMap<>();
|
||||
private Map<String, Short> oldApprovals = new HashMap<>();
|
||||
|
||||
private Op(
|
||||
ProjectState projectState, PatchSet.Id psId, ReviewInput in, NotifyResolver.Result notify) {
|
||||
private Op(ProjectState projectState, PatchSet.Id psId, ReviewInput in) {
|
||||
this.projectState = projectState;
|
||||
this.psId = psId;
|
||||
this.in = in;
|
||||
this.notify = requireNonNull(notify);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -867,6 +871,7 @@ public class PostReview
|
||||
if (message == null) {
|
||||
return;
|
||||
}
|
||||
NotifyResolver.Result notify = ctx.getNotify(notes.getChangeId());
|
||||
if (notify.shouldNotify()) {
|
||||
email
|
||||
.create(notify, notes, ps, user, message, comments, in.message, labelDelta)
|
||||
|
||||
@@ -16,10 +16,12 @@ package com.google.gerrit.server.restapi.change;
|
||||
|
||||
import com.google.gerrit.extensions.api.changes.AddReviewerInput;
|
||||
import com.google.gerrit.extensions.api.changes.AddReviewerResult;
|
||||
import com.google.gerrit.extensions.api.changes.NotifyHandling;
|
||||
import com.google.gerrit.extensions.restapi.BadRequestException;
|
||||
import com.google.gerrit.extensions.restapi.RestApiException;
|
||||
import com.google.gerrit.reviewdb.client.Change;
|
||||
import com.google.gerrit.server.change.ChangeResource;
|
||||
import com.google.gerrit.server.change.NotifyResolver;
|
||||
import com.google.gerrit.server.change.ReviewerAdder;
|
||||
import com.google.gerrit.server.change.ReviewerAdder.ReviewerAddition;
|
||||
import com.google.gerrit.server.change.ReviewerResource;
|
||||
@@ -42,13 +44,18 @@ public class PostReviewers
|
||||
ChangeResource, ReviewerResource, AddReviewerInput, AddReviewerResult> {
|
||||
|
||||
private final ChangeData.Factory changeDataFactory;
|
||||
private final NotifyResolver notifyResolver;
|
||||
private final ReviewerAdder reviewerAdder;
|
||||
|
||||
@Inject
|
||||
PostReviewers(
|
||||
ChangeData.Factory changeDataFactory, RetryHelper retryHelper, ReviewerAdder reviewerAdder) {
|
||||
ChangeData.Factory changeDataFactory,
|
||||
RetryHelper retryHelper,
|
||||
NotifyResolver notifyResolver,
|
||||
ReviewerAdder reviewerAdder) {
|
||||
super(retryHelper);
|
||||
this.changeDataFactory = changeDataFactory;
|
||||
this.notifyResolver = notifyResolver;
|
||||
this.reviewerAdder = reviewerAdder;
|
||||
}
|
||||
|
||||
@@ -67,6 +74,7 @@ public class PostReviewers
|
||||
}
|
||||
try (BatchUpdate bu =
|
||||
updateFactory.create(rsrc.getProject(), rsrc.getUser(), TimeUtil.nowTs())) {
|
||||
bu.setNotify(resolveNotify(rsrc, input));
|
||||
Change.Id id = rsrc.getChange().getId();
|
||||
bu.addOp(id, addition.op);
|
||||
bu.execute();
|
||||
@@ -76,4 +84,14 @@ public class PostReviewers
|
||||
addition.gatherResults(changeDataFactory.create(rsrc.getProject(), rsrc.getId()));
|
||||
return addition.result;
|
||||
}
|
||||
|
||||
private NotifyResolver.Result resolveNotify(ChangeResource rsrc, AddReviewerInput input)
|
||||
throws BadRequestException, OrmException, ConfigInvalidException, IOException {
|
||||
NotifyHandling notifyHandling = input.notify;
|
||||
if (notifyHandling == null) {
|
||||
notifyHandling =
|
||||
rsrc.getChange().isWorkInProgress() ? NotifyHandling.NONE : NotifyHandling.ALL;
|
||||
}
|
||||
return notifyResolver.resolve(notifyHandling, input.notifyDetails);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,6 +99,7 @@ public class PutAssignee extends RetryingRestModifyView<ChangeResource, Assignee
|
||||
bu.addOp(rsrc.getId(), op);
|
||||
|
||||
ReviewerAddition reviewersAddition = addAssigneeAsCC(rsrc, input.assignee);
|
||||
reviewersAddition.op.suppressEmail();
|
||||
bu.addOp(rsrc.getId(), reviewersAddition.op);
|
||||
|
||||
bu.execute();
|
||||
|
||||
@@ -140,8 +140,7 @@ public class PutMessage
|
||||
inserter.setMessage(
|
||||
String.format("Patch Set %s: Commit message was updated.", psId.getId()));
|
||||
inserter.setDescription("Edit commit message");
|
||||
NotifyResolver.Result notify = resolveNotify(input, resource);
|
||||
inserter.setNotify(notify);
|
||||
bu.setNotify(resolveNotify(input, resource));
|
||||
bu.addOp(resource.getChange().getId(), inserter);
|
||||
bu.execute();
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ import com.google.gerrit.server.ChangeUtil;
|
||||
import com.google.gerrit.server.PatchSetUtil;
|
||||
import com.google.gerrit.server.change.ChangeJson;
|
||||
import com.google.gerrit.server.change.ChangeResource;
|
||||
import com.google.gerrit.server.change.NotifyResolver;
|
||||
import com.google.gerrit.server.change.RebaseChangeOp;
|
||||
import com.google.gerrit.server.change.RebaseUtil;
|
||||
import com.google.gerrit.server.change.RebaseUtil.Base;
|
||||
@@ -120,6 +121,8 @@ public class Rebase extends RetryingRestModifyView<RevisionResource, RebaseInput
|
||||
throw new ResourceConflictException(
|
||||
"cannot rebase merge commits or commit with no ancestor");
|
||||
}
|
||||
// TODO(dborowitz): Why no notification? This seems wrong; dig up blame.
|
||||
bu.setNotify(NotifyResolver.Result.none());
|
||||
bu.setRepository(repo, rw, oi);
|
||||
bu.addOp(
|
||||
change.getId(),
|
||||
|
||||
@@ -224,7 +224,6 @@ public class Revert extends RetryingRestModifyView<ChangeResource, RevertInput,
|
||||
.create(changeId, revertCommit, notes.getChange().getDest().get())
|
||||
.setTopic(changeToRevert.getTopic());
|
||||
ins.setMessage("Uploaded patch set 1.");
|
||||
ins.setNotify(notify);
|
||||
|
||||
ReviewerSet reviewerSet = approvalsUtil.getReviewers(notes);
|
||||
|
||||
@@ -239,8 +238,9 @@ public class Revert extends RetryingRestModifyView<ChangeResource, RevertInput,
|
||||
|
||||
try (BatchUpdate bu = updateFactory.create(project, user, now)) {
|
||||
bu.setRepository(git, revWalk, oi);
|
||||
bu.setNotify(notify);
|
||||
bu.insertChange(ins);
|
||||
bu.addOp(changeId, new NotifyOp(changeToRevert, ins, notify));
|
||||
bu.addOp(changeId, new NotifyOp(changeToRevert, ins));
|
||||
bu.addOp(changeToRevert.getId(), new PostRevertedMessageOp(computedChangeId));
|
||||
bu.execute();
|
||||
}
|
||||
@@ -275,12 +275,10 @@ public class Revert extends RetryingRestModifyView<ChangeResource, RevertInput,
|
||||
private class NotifyOp implements BatchUpdateOp {
|
||||
private final Change change;
|
||||
private final ChangeInserter ins;
|
||||
private final NotifyResolver.Result notify;
|
||||
|
||||
NotifyOp(Change change, ChangeInserter ins, NotifyResolver.Result notify) {
|
||||
NotifyOp(Change change, ChangeInserter ins) {
|
||||
this.change = change;
|
||||
this.ins = ins;
|
||||
this.notify = notify;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -289,7 +287,7 @@ public class Revert extends RetryingRestModifyView<ChangeResource, RevertInput,
|
||||
try {
|
||||
RevertedSender cm = revertedSenderFactory.create(ctx.getProject(), change.getId());
|
||||
cm.setFrom(ctx.getAccountId());
|
||||
cm.setNotify(notify);
|
||||
cm.setNotify(ctx.getNotify(change.getId()));
|
||||
cm.send();
|
||||
} catch (Exception err) {
|
||||
logger.atSevere().withCause(err).log(
|
||||
|
||||
@@ -14,9 +14,11 @@
|
||||
|
||||
package com.google.gerrit.server.restapi.change;
|
||||
|
||||
import static com.google.common.base.MoreObjects.firstNonNull;
|
||||
import static com.google.gerrit.extensions.conditions.BooleanCondition.and;
|
||||
import static com.google.gerrit.extensions.conditions.BooleanCondition.or;
|
||||
|
||||
import com.google.gerrit.extensions.api.changes.NotifyHandling;
|
||||
import com.google.gerrit.extensions.restapi.ResourceConflictException;
|
||||
import com.google.gerrit.extensions.restapi.Response;
|
||||
import com.google.gerrit.extensions.restapi.RestApiException;
|
||||
@@ -26,6 +28,7 @@ import com.google.gerrit.reviewdb.client.Change.Status;
|
||||
import com.google.gerrit.server.ChangeUtil;
|
||||
import com.google.gerrit.server.CurrentUser;
|
||||
import com.google.gerrit.server.change.ChangeResource;
|
||||
import com.google.gerrit.server.change.NotifyResolver;
|
||||
import com.google.gerrit.server.change.WorkInProgressOp;
|
||||
import com.google.gerrit.server.change.WorkInProgressOp.Input;
|
||||
import com.google.gerrit.server.permissions.GlobalPermission;
|
||||
@@ -77,6 +80,7 @@ public class SetReadyForReview extends RetryingRestModifyView<ChangeResource, In
|
||||
|
||||
try (BatchUpdate bu =
|
||||
updateFactory.create(rsrc.getProject(), rsrc.getUser(), TimeUtil.nowTs())) {
|
||||
bu.setNotify(NotifyResolver.Result.create(firstNonNull(input.notify, NotifyHandling.ALL)));
|
||||
bu.addOp(rsrc.getChange().getId(), opFactory.create(false, input));
|
||||
bu.execute();
|
||||
return Response.ok("");
|
||||
|
||||
@@ -14,9 +14,11 @@
|
||||
|
||||
package com.google.gerrit.server.restapi.change;
|
||||
|
||||
import static com.google.common.base.MoreObjects.firstNonNull;
|
||||
import static com.google.gerrit.extensions.conditions.BooleanCondition.and;
|
||||
import static com.google.gerrit.extensions.conditions.BooleanCondition.or;
|
||||
|
||||
import com.google.gerrit.extensions.api.changes.NotifyHandling;
|
||||
import com.google.gerrit.extensions.restapi.ResourceConflictException;
|
||||
import com.google.gerrit.extensions.restapi.Response;
|
||||
import com.google.gerrit.extensions.restapi.RestApiException;
|
||||
@@ -26,6 +28,7 @@ import com.google.gerrit.reviewdb.client.Change.Status;
|
||||
import com.google.gerrit.server.ChangeUtil;
|
||||
import com.google.gerrit.server.CurrentUser;
|
||||
import com.google.gerrit.server.change.ChangeResource;
|
||||
import com.google.gerrit.server.change.NotifyResolver;
|
||||
import com.google.gerrit.server.change.WorkInProgressOp;
|
||||
import com.google.gerrit.server.change.WorkInProgressOp.Input;
|
||||
import com.google.gerrit.server.permissions.GlobalPermission;
|
||||
@@ -77,6 +80,7 @@ public class SetWorkInProgress extends RetryingRestModifyView<ChangeResource, In
|
||||
|
||||
try (BatchUpdate bu =
|
||||
updateFactory.create(rsrc.getProject(), rsrc.getUser(), TimeUtil.nowTs())) {
|
||||
bu.setNotify(NotifyResolver.Result.create(firstNonNull(input.notify, NotifyHandling.NONE)));
|
||||
bu.addOp(rsrc.getChange().getId(), opFactory.create(true, input));
|
||||
bu.execute();
|
||||
return Response.ok("");
|
||||
|
||||
@@ -533,7 +533,7 @@ public class MergeOp implements AutoCloseable {
|
||||
orm.close();
|
||||
}
|
||||
orm = ormProvider.get();
|
||||
orm.setContext(ts, caller);
|
||||
orm.setContext(ts, caller, notify);
|
||||
}
|
||||
|
||||
private ChangeSet reloadChanges(ChangeSet changeSet) {
|
||||
@@ -676,7 +676,6 @@ public class MergeOp implements AutoCloseable {
|
||||
commitStatus,
|
||||
submissionId,
|
||||
submitInput,
|
||||
notify,
|
||||
submoduleOp,
|
||||
dryrun);
|
||||
strategies.add(strategy);
|
||||
|
||||
@@ -15,12 +15,14 @@
|
||||
package com.google.gerrit.server.submit;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.gerrit.reviewdb.client.Branch;
|
||||
import com.google.gerrit.reviewdb.client.Project;
|
||||
import com.google.gerrit.reviewdb.client.RefNames;
|
||||
import com.google.gerrit.server.IdentifiedUser;
|
||||
import com.google.gerrit.server.change.NotifyResolver;
|
||||
import com.google.gerrit.server.git.CodeReviewCommit;
|
||||
import com.google.gerrit.server.git.CodeReviewCommit.CodeReviewRevWalk;
|
||||
import com.google.gerrit.server.git.GitRepositoryManager;
|
||||
@@ -110,6 +112,7 @@ public class MergeOpRepoManager implements AutoCloseable {
|
||||
batchUpdateFactory
|
||||
.create(getProjectName(), caller, ts)
|
||||
.setRepository(repo, rw, ins)
|
||||
.setNotify(notify)
|
||||
.setOnSubmitValidators(onSubmitValidatorsFactory.create());
|
||||
}
|
||||
return update;
|
||||
@@ -158,6 +161,7 @@ public class MergeOpRepoManager implements AutoCloseable {
|
||||
|
||||
private Timestamp ts;
|
||||
private IdentifiedUser caller;
|
||||
private NotifyResolver.Result notify;
|
||||
|
||||
@Inject
|
||||
MergeOpRepoManager(
|
||||
@@ -173,9 +177,10 @@ public class MergeOpRepoManager implements AutoCloseable {
|
||||
openRepos = new HashMap<>();
|
||||
}
|
||||
|
||||
public void setContext(Timestamp ts, IdentifiedUser caller) {
|
||||
this.ts = ts;
|
||||
this.caller = caller;
|
||||
public void setContext(Timestamp ts, IdentifiedUser caller, NotifyResolver.Result notify) {
|
||||
this.ts = requireNonNull(ts);
|
||||
this.caller = requireNonNull(caller);
|
||||
this.notify = requireNonNull(notify);
|
||||
}
|
||||
|
||||
public OpenRepo getRepo(Project.NameKey project) throws NoSuchProjectException, IOException {
|
||||
@@ -200,7 +205,7 @@ public class MergeOpRepoManager implements AutoCloseable {
|
||||
throws NoSuchProjectException, IOException {
|
||||
List<BatchUpdate> updates = new ArrayList<>(projects.size());
|
||||
for (Project.NameKey project : projects) {
|
||||
updates.add(getRepo(project).getUpdate().setRefLogMessage("merged"));
|
||||
updates.add(getRepo(project).getUpdate().setNotify(notify).setRefLogMessage("merged"));
|
||||
}
|
||||
return updates;
|
||||
}
|
||||
|
||||
@@ -29,7 +29,6 @@ import com.google.gerrit.server.IdentifiedUser;
|
||||
import com.google.gerrit.server.PatchSetUtil;
|
||||
import com.google.gerrit.server.account.AccountCache;
|
||||
import com.google.gerrit.server.change.LabelNormalizer;
|
||||
import com.google.gerrit.server.change.NotifyResolver;
|
||||
import com.google.gerrit.server.change.RebaseChangeOp;
|
||||
import com.google.gerrit.server.change.TestSubmitInput;
|
||||
import com.google.gerrit.server.extensions.events.ChangeMerged;
|
||||
@@ -92,7 +91,6 @@ public abstract class SubmitStrategy {
|
||||
Set<CodeReviewCommit> incoming,
|
||||
RequestId submissionId,
|
||||
SubmitInput submitInput,
|
||||
NotifyResolver.Result notify,
|
||||
SubmoduleOp submoduleOp,
|
||||
boolean dryrun);
|
||||
}
|
||||
@@ -124,7 +122,6 @@ public abstract class SubmitStrategy {
|
||||
final RequestId submissionId;
|
||||
final SubmitType submitType;
|
||||
final SubmitInput submitInput;
|
||||
final NotifyResolver.Result notify;
|
||||
final SubmoduleOp submoduleOp;
|
||||
|
||||
final ProjectState project;
|
||||
@@ -163,7 +160,6 @@ public abstract class SubmitStrategy {
|
||||
@Assisted RequestId submissionId,
|
||||
@Assisted SubmitType submitType,
|
||||
@Assisted SubmitInput submitInput,
|
||||
@Assisted NotifyResolver.Result notify,
|
||||
@Assisted SubmoduleOp submoduleOp,
|
||||
@Assisted boolean dryrun) {
|
||||
this.accountCache = accountCache;
|
||||
@@ -192,7 +188,6 @@ public abstract class SubmitStrategy {
|
||||
this.submissionId = submissionId;
|
||||
this.submitType = submitType;
|
||||
this.submitInput = submitInput;
|
||||
this.notify = notify;
|
||||
this.submoduleOp = submoduleOp;
|
||||
this.dryrun = dryrun;
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@ import com.google.gerrit.extensions.api.changes.SubmitInput;
|
||||
import com.google.gerrit.extensions.client.SubmitType;
|
||||
import com.google.gerrit.reviewdb.client.Branch;
|
||||
import com.google.gerrit.server.IdentifiedUser;
|
||||
import com.google.gerrit.server.change.NotifyResolver;
|
||||
import com.google.gerrit.server.git.CodeReviewCommit;
|
||||
import com.google.gerrit.server.git.CodeReviewCommit.CodeReviewRevWalk;
|
||||
import com.google.gerrit.server.git.MergeTip;
|
||||
@@ -55,7 +54,6 @@ public class SubmitStrategyFactory {
|
||||
CommitStatus commitStatus,
|
||||
RequestId submissionId,
|
||||
SubmitInput submitInput,
|
||||
NotifyResolver.Result notify,
|
||||
SubmoduleOp submoduleOp,
|
||||
boolean dryrun)
|
||||
throws IntegrationException {
|
||||
@@ -72,7 +70,6 @@ public class SubmitStrategyFactory {
|
||||
incoming,
|
||||
submissionId,
|
||||
submitInput,
|
||||
notify,
|
||||
submoduleOp,
|
||||
dryrun);
|
||||
switch (submitType) {
|
||||
|
||||
@@ -501,7 +501,7 @@ abstract class SubmitStrategyOp implements BatchUpdateOp {
|
||||
// have failed fast in one of the other steps.
|
||||
try {
|
||||
args.mergedSenderFactory
|
||||
.create(ctx.getProject(), getId(), submitter.getAccountId(), args.notify)
|
||||
.create(ctx.getProject(), getId(), submitter.getAccountId(), ctx.getNotify(getId()))
|
||||
.sendAsync();
|
||||
} catch (Exception e) {
|
||||
logger.atSevere().withCause(e).log("Cannot email merged notification for %s", getId());
|
||||
|
||||
@@ -30,6 +30,7 @@ import com.google.common.collect.MultimapBuilder;
|
||||
import com.google.common.collect.Multiset;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import com.google.gerrit.common.Nullable;
|
||||
import com.google.gerrit.extensions.api.changes.NotifyHandling;
|
||||
import com.google.gerrit.extensions.config.FactoryModule;
|
||||
import com.google.gerrit.extensions.restapi.ResourceConflictException;
|
||||
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
|
||||
@@ -40,6 +41,7 @@ import com.google.gerrit.reviewdb.client.Project;
|
||||
import com.google.gerrit.server.CurrentUser;
|
||||
import com.google.gerrit.server.GerritPersonIdent;
|
||||
import com.google.gerrit.server.account.AccountState;
|
||||
import com.google.gerrit.server.change.NotifyResolver;
|
||||
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
|
||||
import com.google.gerrit.server.git.GitRepositoryManager;
|
||||
import com.google.gerrit.server.git.validators.OnSubmitValidators;
|
||||
@@ -229,6 +231,12 @@ public class BatchUpdate implements AutoCloseable {
|
||||
public CurrentUser getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NotifyResolver.Result getNotify(Change.Id changeId) {
|
||||
NotifyHandling notifyHandling = perChangeNotifyHandling.get(changeId);
|
||||
return notifyHandling != null ? notify.withHandling(notifyHandling) : notify;
|
||||
}
|
||||
}
|
||||
|
||||
private class RepoContextImpl extends ContextImpl implements RepoContext {
|
||||
@@ -302,12 +310,14 @@ public class BatchUpdate implements AutoCloseable {
|
||||
MultimapBuilder.linkedHashKeys().arrayListValues().build();
|
||||
private final Map<Change.Id, Change> newChanges = new HashMap<>();
|
||||
private final List<RepoOnlyOp> repoOnlyOps = new ArrayList<>();
|
||||
private final Map<Change.Id, NotifyHandling> perChangeNotifyHandling = new HashMap<>();
|
||||
|
||||
private RepoView repoView;
|
||||
private BatchRefUpdate batchRefUpdate;
|
||||
private OnSubmitValidators onSubmitValidators;
|
||||
private PushCertificate pushCert;
|
||||
private String refLogMessage;
|
||||
private NotifyResolver.Result notify = NotifyResolver.Result.all();
|
||||
|
||||
@Inject
|
||||
BatchUpdate(
|
||||
@@ -364,6 +374,32 @@ public class BatchUpdate implements AutoCloseable {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default notification settings for all changes in the batch.
|
||||
*
|
||||
* @param notify notification settings.
|
||||
* @return this.
|
||||
*/
|
||||
public BatchUpdate setNotify(NotifyResolver.Result notify) {
|
||||
this.notify = requireNonNull(notify);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override the {@link NotifyHandling} on a per-change basis.
|
||||
*
|
||||
* <p>Only the handling enum can be overridden; all changes share the same value for {@link
|
||||
* NotifyResolver.Result#accounts()}.
|
||||
*
|
||||
* @param changeId change ID.
|
||||
* @param notifyHandling notify handling.
|
||||
* @return this.
|
||||
*/
|
||||
public BatchUpdate setNotifyHandling(Change.Id changeId, NotifyHandling notifyHandling) {
|
||||
this.perChangeNotifyHandling.put(changeId, requireNonNull(notifyHandling));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a validation step for intended ref operations, which will be performed at the end of {@link
|
||||
* RepoOnlyOp#updateRepo(RepoContext)} step.
|
||||
|
||||
@@ -17,10 +17,12 @@ package com.google.gerrit.server.update;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import com.google.gerrit.reviewdb.client.Account;
|
||||
import com.google.gerrit.reviewdb.client.Change;
|
||||
import com.google.gerrit.reviewdb.client.Project;
|
||||
import com.google.gerrit.server.CurrentUser;
|
||||
import com.google.gerrit.server.IdentifiedUser;
|
||||
import com.google.gerrit.server.account.AccountState;
|
||||
import com.google.gerrit.server.change.NotifyResolver;
|
||||
import java.io.IOException;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.TimeZone;
|
||||
@@ -85,6 +87,18 @@ public interface Context {
|
||||
*/
|
||||
CurrentUser getUser();
|
||||
|
||||
/**
|
||||
* Get the notification settings configured by the caller.
|
||||
*
|
||||
* <p>If there are multiple changes in a batch, they may have different settings. For example, WIP
|
||||
* changes may have reduced {@code NotifyHandling} levels, and may be in a batch with non-WIP
|
||||
* changes.
|
||||
*
|
||||
* @param changeId change ID
|
||||
* @return notification settings.
|
||||
*/
|
||||
NotifyResolver.Result getNotify(Change.Id changeId);
|
||||
|
||||
/**
|
||||
* Get the identified user performing the update.
|
||||
*
|
||||
|
||||
@@ -749,11 +749,11 @@ public class ConsistencyCheckerIT extends AbstractDaemonTest {
|
||||
ChangeInserter ins;
|
||||
try (BatchUpdate bu = newUpdate(owner.getId())) {
|
||||
RevCommit commit = patchSetCommit(new PatchSet.Id(id, 1));
|
||||
bu.setNotify(NotifyResolver.Result.none());
|
||||
ins =
|
||||
changeInserterFactory
|
||||
.create(id, commit, dest)
|
||||
.setValidate(false)
|
||||
.setNotify(NotifyResolver.Result.none())
|
||||
.setFireRevisionCreated(false)
|
||||
.setSendMail(false);
|
||||
bu.insertChange(ins).execute();
|
||||
@@ -773,12 +773,12 @@ public class ConsistencyCheckerIT extends AbstractDaemonTest {
|
||||
private ChangeNotes incrementPatchSet(ChangeNotes notes, RevCommit commit) throws Exception {
|
||||
PatchSetInserter ins;
|
||||
try (BatchUpdate bu = newUpdate(notes.getChange().getOwner())) {
|
||||
bu.setNotify(NotifyResolver.Result.none());
|
||||
ins =
|
||||
patchSetInserterFactory
|
||||
.create(notes, nextPatchSetId(notes), commit)
|
||||
.setValidate(false)
|
||||
.setFireRevisionCreated(false)
|
||||
.setNotify(NotifyResolver.Result.none());
|
||||
.setFireRevisionCreated(false);
|
||||
bu.addOp(notes.getChangeId(), ins).execute();
|
||||
}
|
||||
return reload(notes);
|
||||
|
||||
@@ -3176,7 +3176,6 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
|
||||
PatchSetInserter inserter =
|
||||
patchSetFactory
|
||||
.create(changeNotesFactory.createChecked(c), new PatchSet.Id(c.getId(), n), commit)
|
||||
.setNotify(NotifyResolver.Result.none())
|
||||
.setFireRevisionCreated(false)
|
||||
.setValidate(false);
|
||||
try (BatchUpdate bu = updateFactory.create(c.getProject(), user, TimeUtil.nowTs());
|
||||
@@ -3184,6 +3183,7 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
|
||||
ObjectReader reader = oi.newReader();
|
||||
RevWalk rw = new RevWalk(reader)) {
|
||||
bu.setRepository(repo.getRepository(), rw, oi);
|
||||
bu.setNotify(NotifyResolver.Result.none());
|
||||
bu.addOp(c.getId(), inserter);
|
||||
bu.execute();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user