summaryrefslogtreecommitdiffstats
path: root/gerrit-server/src/main/java/com/google/gerrit/server/mail/receive/MailProcessor.java
diff options
context:
space:
mode:
Diffstat (limited to 'gerrit-server/src/main/java/com/google/gerrit/server/mail/receive/MailProcessor.java')
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/mail/receive/MailProcessor.java192
1 files changed, 114 insertions, 78 deletions
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/receive/MailProcessor.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/receive/MailProcessor.java
index 020c74b7c6..ff09f53c8b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/receive/MailProcessor.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/receive/MailProcessor.java
@@ -36,20 +36,22 @@ import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.ChangeMessagesUtil;
import com.google.gerrit.server.CommentsUtil;
import com.google.gerrit.server.PatchSetUtil;
-import com.google.gerrit.server.account.AccountByEmailCache;
import com.google.gerrit.server.account.AccountCache;
+import com.google.gerrit.server.account.Emails;
import com.google.gerrit.server.change.EmailReviewComments;
import com.google.gerrit.server.config.CanonicalWebUrl;
import com.google.gerrit.server.extensions.events.CommentAdded;
import com.google.gerrit.server.mail.MailFilter;
+import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.patch.PatchListCache;
-import com.google.gerrit.server.project.ChangeControl;
+import com.google.gerrit.server.patch.PatchListNotAvailableException;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.InternalChangeQuery;
import com.google.gerrit.server.update.BatchUpdate;
import com.google.gerrit.server.update.BatchUpdateOp;
import com.google.gerrit.server.update.ChangeContext;
import com.google.gerrit.server.update.Context;
+import com.google.gerrit.server.update.RetryHelper;
import com.google.gerrit.server.update.UpdateException;
import com.google.gerrit.server.util.ManualRequestContext;
import com.google.gerrit.server.util.OneOffRequestContext;
@@ -57,6 +59,7 @@ import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
@@ -67,12 +70,13 @@ import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+/** A service that can attach the comments from a {@link MailMessage} to a change. */
@Singleton
public class MailProcessor {
private static final Logger log = LoggerFactory.getLogger(MailProcessor.class);
- private final AccountByEmailCache accountByEmailCache;
- private final BatchUpdate.Factory buf;
+ private final Emails emails;
+ private final RetryHelper retryHelper;
private final ChangeMessagesUtil changeMessagesUtil;
private final CommentsUtil commentsUtil;
private final OneOffRequestContext oneOffRequestContext;
@@ -88,8 +92,8 @@ public class MailProcessor {
@Inject
public MailProcessor(
- AccountByEmailCache accountByEmailCache,
- BatchUpdate.Factory buf,
+ Emails emails,
+ RetryHelper retryHelper,
ChangeMessagesUtil changeMessagesUtil,
CommentsUtil commentsUtil,
OneOffRequestContext oneOffRequestContext,
@@ -102,8 +106,8 @@ public class MailProcessor {
CommentAdded commentAdded,
AccountCache accountCache,
@CanonicalWebUrl Provider<String> canonicalUrl) {
- this.accountByEmailCache = accountByEmailCache;
- this.buf = buf;
+ this.emails = emails;
+ this.retryHelper = retryHelper;
this.changeMessagesUtil = changeMessagesUtil;
this.commentsUtil = commentsUtil;
this.oneOffRequestContext = oneOffRequestContext;
@@ -119,12 +123,20 @@ public class MailProcessor {
}
/**
- * Parse comments from MailMessage and persist them on the change.
+ * Parses comments from a {@link MailMessage} and persists them on the change.
*
- * @param message MailMessage to process.
- * @throws OrmException
+ * @param message {@link MailMessage} to process
*/
- public void process(MailMessage message) throws OrmException {
+ public void process(MailMessage message) throws RestApiException, UpdateException {
+ retryHelper.execute(
+ buf -> {
+ processImpl(buf, message);
+ return null;
+ });
+ }
+
+ private void processImpl(BatchUpdate.Factory buf, MailMessage message)
+ throws OrmException, UpdateException, RestApiException, IOException {
for (DynamicMap.Entry<MailFilter> filter : mailFilters) {
if (!filter.getProvider().get().shouldProcessMessage(message)) {
log.warn(
@@ -145,22 +157,28 @@ public class MailProcessor {
return;
}
- Set<Account.Id> accounts = accountByEmailCache.get(metadata.author);
- if (accounts.size() != 1) {
+ Set<Account.Id> accountIds = emails.getAccountFor(metadata.author);
+ if (accountIds.size() != 1) {
log.error(
"Address {} could not be matched to a unique account. It was matched to {}."
+ " Will delete message.",
metadata.author,
- accounts);
+ accountIds);
return;
}
- Account.Id account = accounts.iterator().next();
+ Account.Id account = accountIds.iterator().next();
if (!accountCache.get(account).getAccount().isActive()) {
log.warn("Mail: Account {} is inactive. Will delete message.", account);
return;
}
- try (ManualRequestContext ctx = oneOffRequestContext.openAs(account)) {
+ persistComments(buf, message, metadata, account);
+ }
+
+ private void persistComments(
+ BatchUpdate.Factory buf, MailMessage message, MailMetadata metadata, Account.Id sender)
+ throws OrmException, UpdateException, RestApiException {
+ try (ManualRequestContext ctx = oneOffRequestContext.openAs(sender)) {
List<ChangeData> changeDataList =
queryProvider.get().byLegacyChangeId(new Change.Id(metadata.changeNumber));
if (changeDataList.size() != 1) {
@@ -203,11 +221,7 @@ public class MailProcessor {
Op o = new Op(new PatchSet.Id(cd.getId(), metadata.patchSet), parsedComments, message.id());
BatchUpdate batchUpdate = buf.create(cd.db(), project, ctx.getUser(), TimeUtil.nowTs());
batchUpdate.addOp(cd.getId(), o);
- try {
- batchUpdate.execute();
- } catch (UpdateException | RestApiException e) {
- throw new OrmException(e);
- }
+ batchUpdate.execute();
}
}
@@ -218,7 +232,7 @@ public class MailProcessor {
private ChangeMessage changeMessage;
private List<Comment> comments;
private PatchSet patchSet;
- private ChangeControl changeControl;
+ private ChangeNotes notes;
private Op(PatchSet.Id psId, List<MailComment> parsedComments, String messageId) {
this.psId = psId;
@@ -228,69 +242,23 @@ public class MailProcessor {
@Override
public boolean updateChange(ChangeContext ctx)
- throws OrmException, UnprocessableEntityException {
- changeControl = ctx.getControl();
+ throws OrmException, UnprocessableEntityException, PatchListNotAvailableException {
patchSet = psUtil.get(ctx.getDb(), ctx.getNotes(), psId);
+ notes = ctx.getNotes();
if (patchSet == null) {
throw new OrmException("patch set not found: " + psId);
}
- String changeMsg = "Patch Set " + psId.get() + ":";
- if (parsedComments.get(0).type == MailComment.CommentType.CHANGE_MESSAGE) {
- // Add a blank line after Patch Set to follow the default format
- if (parsedComments.size() > 1) {
- changeMsg += "\n\n" + numComments(parsedComments.size() - 1);
- }
- changeMsg += "\n\n" + parsedComments.get(0).message;
- } else {
- changeMsg += "\n\n" + numComments(parsedComments.size());
- }
-
- changeMessage = ChangeMessagesUtil.newMessage(ctx, changeMsg, tag);
+ changeMessage = generateChangeMessage(ctx);
changeMessagesUtil.addChangeMessage(ctx.getDb(), ctx.getUpdate(psId), changeMessage);
+
comments = new ArrayList<>();
for (MailComment c : parsedComments) {
if (c.type == MailComment.CommentType.CHANGE_MESSAGE) {
continue;
}
-
- String fileName;
- // The patch set that this comment is based on is different if this
- // comment was sent in reply to a comment on a previous patch set.
- PatchSet psForComment;
- Side side;
- if (c.inReplyTo != null) {
- fileName = c.inReplyTo.key.filename;
- psForComment =
- psUtil.get(
- ctx.getDb(),
- ctx.getNotes(),
- new PatchSet.Id(ctx.getChange().getId(), c.inReplyTo.key.patchSetId));
- side = Side.fromShort(c.inReplyTo.side);
- } else {
- fileName = c.fileName;
- psForComment = patchSet;
- side = Side.REVISION;
- }
-
- Comment comment =
- commentsUtil.newComment(
- ctx,
- fileName,
- psForComment.getId(),
- (short) side.ordinal(),
- c.message,
- false,
- null);
- comment.tag = tag;
- if (c.inReplyTo != null) {
- comment.parentUuid = c.inReplyTo.key.uuid;
- comment.lineNbr = c.inReplyTo.lineNbr;
- comment.range = c.inReplyTo.range;
- comment.unresolved = c.inReplyTo.unresolved;
- }
- CommentsUtil.setCommentRevId(comment, patchListCache, ctx.getChange(), psForComment);
- comments.add(comment);
+ comments.add(
+ persistentCommentFromMailComment(ctx, c, targetPatchSetForComment(ctx, c, patchSet)));
}
commentsUtil.putComments(
ctx.getDb(),
@@ -312,7 +280,7 @@ public class MailProcessor {
.create(
NotifyHandling.ALL,
ArrayListMultimap.create(),
- changeControl.getNotes(),
+ notes,
patchSet,
ctx.getUser().asIdentifiedUser(),
changeMessage,
@@ -323,12 +291,19 @@ public class MailProcessor {
// Get previous approvals from this user
Map<String, Short> approvals = new HashMap<>();
approvalsUtil
- .byPatchSetUser(ctx.getDb(), changeControl, psId, ctx.getAccountId())
+ .byPatchSetUser(
+ ctx.getDb(),
+ notes,
+ ctx.getUser(),
+ psId,
+ ctx.getAccountId(),
+ ctx.getRevWalk(),
+ ctx.getRepoView().getConfig())
.forEach(a -> approvals.put(a.getLabel(), a.getValue()));
// Fire Gerrit event. Note that approvals can't be granted via email, so old and new approvals
// are always the same here.
commentAdded.fire(
- changeControl.getChange(),
+ notes.getChange(),
patchSet,
ctx.getAccount(),
changeMessage.getMessage(),
@@ -336,6 +311,67 @@ public class MailProcessor {
approvals,
ctx.getWhen());
}
+
+ private ChangeMessage generateChangeMessage(ChangeContext ctx) {
+ String changeMsg = "Patch Set " + psId.get() + ":";
+ if (parsedComments.get(0).type == MailComment.CommentType.CHANGE_MESSAGE) {
+ // Add a blank line after Patch Set to follow the default format
+ if (parsedComments.size() > 1) {
+ changeMsg += "\n\n" + numComments(parsedComments.size() - 1);
+ }
+ changeMsg += "\n\n" + parsedComments.get(0).message;
+ } else {
+ changeMsg += "\n\n" + numComments(parsedComments.size());
+ }
+ return ChangeMessagesUtil.newMessage(ctx, changeMsg, tag);
+ }
+
+ private PatchSet targetPatchSetForComment(
+ ChangeContext ctx, MailComment mailComment, PatchSet current) throws OrmException {
+ if (mailComment.inReplyTo != null) {
+ return psUtil.get(
+ ctx.getDb(),
+ ctx.getNotes(),
+ new PatchSet.Id(ctx.getChange().getId(), mailComment.inReplyTo.key.patchSetId));
+ }
+ return current;
+ }
+
+ private Comment persistentCommentFromMailComment(
+ ChangeContext ctx, MailComment mailComment, PatchSet patchSetForComment)
+ throws OrmException, UnprocessableEntityException, PatchListNotAvailableException {
+ String fileName;
+ // The patch set that this comment is based on is different if this
+ // comment was sent in reply to a comment on a previous patch set.
+ Side side;
+ if (mailComment.inReplyTo != null) {
+ fileName = mailComment.inReplyTo.key.filename;
+ side = Side.fromShort(mailComment.inReplyTo.side);
+ } else {
+ fileName = mailComment.fileName;
+ side = Side.REVISION;
+ }
+
+ Comment comment =
+ commentsUtil.newComment(
+ ctx,
+ fileName,
+ patchSetForComment.getId(),
+ (short) side.ordinal(),
+ mailComment.message,
+ false,
+ null);
+
+ comment.tag = tag;
+ if (mailComment.inReplyTo != null) {
+ comment.parentUuid = mailComment.inReplyTo.key.uuid;
+ comment.lineNbr = mailComment.inReplyTo.lineNbr;
+ comment.range = mailComment.inReplyTo.range;
+ comment.unresolved = mailComment.inReplyTo.unresolved;
+ }
+ CommentsUtil.setCommentRevId(comment, patchListCache, ctx.getChange(), patchSetForComment);
+ return comment;
+ }
}
private static boolean useHtmlParser(MailMessage m) {