Support ChangeUpdates without providing a CurrentUser

We want to thin out the dependency stack of ChangeRebuilderImpl in our
custom online rebuilding implementation. Using full ChangeControls and
IdentifiedUsers brings in way more than we need to just create author
idents containing the user's full name.

Rework AbstractChangeUpdate to just take an Account.Id and
prepopulated PersonIdent, and teach ChangeRebuilderImpl to use that
path.

Change-Id: I79af43ff21fbd27ad682134d37fae75f043a586a
This commit is contained in:
Dave Borowitz 2016-04-01 16:37:02 -04:00
parent 5a18df0286
commit ebdba50b40
10 changed files with 217 additions and 147 deletions

View File

@ -72,10 +72,9 @@ public class ChangeMessagesUtil {
public void addChangeMessage(ReviewDb db, ChangeUpdate update, public void addChangeMessage(ReviewDb db, ChangeUpdate update,
ChangeMessage changeMessage) throws OrmException { ChangeMessage changeMessage) throws OrmException {
checkState( checkState(
Objects.equals(changeMessage.getAuthor(), Objects.equals(changeMessage.getAuthor(), update.getAccountId()),
update.getUser().getAccountId()), "cannot store change message by %s in update by %s",
"cannot store change message of %s in update of %s", changeMessage.getAuthor(), update.getAccountId());
changeMessage.getAuthor(), update.getUser().getAccountId());
update.setChangeMessage(changeMessage.getMessage()); update.setChangeMessage(changeMessage.getMessage());
db.changeMessages().insert(Collections.singleton(changeMessage)); db.changeMessages().insert(Collections.singleton(changeMessage));
} }

View File

@ -96,7 +96,7 @@ public class PatchSetUtil {
PatchSet ps = new PatchSet(psId); PatchSet ps = new PatchSet(psId);
ps.setRevision(new RevId(commit.name())); ps.setRevision(new RevId(commit.name()));
ps.setUploader(update.getUser().getAccountId()); ps.setUploader(update.getAccountId());
ps.setCreatedOn(new Timestamp(update.getWhen().getTime())); ps.setCreatedOn(new Timestamp(update.getWhen().getTime()));
ps.setDraft(draft); ps.setDraft(draft);
ps.setGroups(groups); ps.setGroups(groups);

View File

@ -93,7 +93,7 @@ public class SetHashtagsOp extends BatchUpdate.Op {
} }
change = ctx.getChange(); change = ctx.getChange();
ChangeUpdate update = ctx.getUpdate(change.currentPatchSetId()); ChangeUpdate update = ctx.getUpdate(change.currentPatchSetId());
ChangeNotes notes = update.getChangeNotes().load(); ChangeNotes notes = update.getNotes().load();
Set<String> existingHashtags = notes.getHashtags(); Set<String> existingHashtags = notes.getHashtags();
Set<String> updated = new HashSet<>(); Set<String> updated = new HashSet<>();

View File

@ -15,6 +15,7 @@
package com.google.gerrit.server.notedb; package com.google.gerrit.server.notedb;
import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
import com.google.gerrit.reviewdb.client.Account; import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.reviewdb.client.Change;
@ -23,7 +24,6 @@ import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.CurrentUser; import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser; import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.InternalUser; import com.google.gerrit.server.InternalUser;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.project.ChangeControl; import com.google.gerrit.server.project.ChangeControl;
import com.google.gwtorm.server.OrmException; import com.google.gwtorm.server.OrmException;
@ -41,63 +41,69 @@ import java.util.Date;
/** A single delta related to a specific patch-set of a change. */ /** A single delta related to a specific patch-set of a change. */
public abstract class AbstractChangeUpdate { public abstract class AbstractChangeUpdate {
protected final NotesMigration migration; protected final NotesMigration migration;
protected final GitRepositoryManager repoManager;
protected final ChangeControl ctl;
protected final String anonymousCowardName;
protected final ChangeNoteUtil noteUtil; protected final ChangeNoteUtil noteUtil;
protected final String anonymousCowardName;
protected final ChangeNotes notes;
protected final Account.Id accountId;
protected final PersonIdent authorIdent;
protected final Date when; protected final Date when;
private final PersonIdent serverIdent; private final PersonIdent serverIdent;
protected PatchSet.Id psId; protected PatchSet.Id psId;
private ObjectId result; private ObjectId result;
protected AbstractChangeUpdate(NotesMigration migration, protected AbstractChangeUpdate(
GitRepositoryManager repoManager, NotesMigration migration,
ChangeControl ctl, ChangeControl ctl,
PersonIdent serverIdent, PersonIdent serverIdent,
String anonymousCowardName, String anonymousCowardName,
ChangeNoteUtil noteUtil, ChangeNoteUtil noteUtil,
Date when) { Date when) {
this(
migration,
noteUtil,
serverIdent,
anonymousCowardName,
ctl.getNotes(),
accountId(ctl.getUser()),
ident(noteUtil, serverIdent, anonymousCowardName, ctl.getUser(), when),
when);
}
protected AbstractChangeUpdate(
NotesMigration migration,
ChangeNoteUtil noteUtil,
PersonIdent serverIdent,
String anonymousCowardName,
ChangeNotes notes,
Account.Id accountId,
PersonIdent authorIdent,
Date when) {
this.migration = migration; this.migration = migration;
this.repoManager = repoManager;
this.ctl = ctl;
this.serverIdent = serverIdent;
this.anonymousCowardName = anonymousCowardName;
this.noteUtil = noteUtil; this.noteUtil = noteUtil;
this.serverIdent = new PersonIdent(serverIdent, when);
this.anonymousCowardName = anonymousCowardName;
this.notes = notes;
this.accountId = accountId;
this.authorIdent = authorIdent;
this.when = when; this.when = when;
}
private static void checkUserType(CurrentUser user) {
checkArgument( checkArgument(
(ctl.getUser() instanceof IdentifiedUser) (user instanceof IdentifiedUser) || (user instanceof InternalUser),
|| (ctl.getUser() instanceof InternalUser), "user must be IdentifiedUser or InternalUser: %s", user);
"user must be IdentifiedUser or InternalUser: %s", ctl.getUser());
} }
public ChangeNotes getChangeNotes() { private static Account.Id accountId(CurrentUser u) {
return ctl.getNotes(); checkUserType(u);
return (u instanceof IdentifiedUser) ? u.getAccountId() : null;
} }
public Change getChange() { private static PersonIdent ident(ChangeNoteUtil noteUtil,
return ctl.getChange(); PersonIdent serverIdent, String anonymousCowardName, CurrentUser u,
} Date when) {
checkUserType(u);
public Date getWhen() {
return when;
}
public CurrentUser getUser() {
return ctl.getUser();
}
public PatchSet.Id getPatchSetId() {
return psId;
}
public void setPatchSetId(PatchSet.Id psId) {
checkArgument(psId == null || psId.getParentKey().equals(ctl.getId()));
this.psId = psId;
}
private PersonIdent newAuthorIdent() {
CurrentUser u = getUser();
if (u instanceof IdentifiedUser) { if (u instanceof IdentifiedUser) {
return noteUtil.newIdent(u.asIdentifiedUser().getAccount(), when, return noteUtil.newIdent(u.asIdentifiedUser().getAccount(), when,
serverIdent, anonymousCowardName); serverIdent, anonymousCowardName);
@ -107,6 +113,42 @@ public abstract class AbstractChangeUpdate {
throw new IllegalStateException(); throw new IllegalStateException();
} }
public Change.Id getId() {
return notes.getChangeId();
}
public ChangeNotes getNotes() {
return notes;
}
public Change getChange() {
return notes.getChange();
}
public Date getWhen() {
return when;
}
public PatchSet.Id getPatchSetId() {
return psId;
}
public void setPatchSetId(PatchSet.Id psId) {
checkArgument(psId == null || psId.getParentKey().equals(getId()));
this.psId = psId;
}
public Account.Id getAccountId() {
checkState(accountId != null,
"author identity for %s is not from an IdentifiedUser: %s",
getClass().getSimpleName(), authorIdent.toExternalString());
return accountId;
}
public Account.Id getNullableAccountId() {
return accountId;
}
protected PersonIdent newIdent(Account author, Date when) { protected PersonIdent newIdent(Account author, Date when) {
return noteUtil.newIdent(author, when, serverIdent, anonymousCowardName); return noteUtil.newIdent(author, when, serverIdent, anonymousCowardName);
} }
@ -145,7 +187,7 @@ public abstract class AbstractChangeUpdate {
result = z; result = z;
return z; // Impl intends to delete the ref. return z; // Impl intends to delete the ref.
} }
cb.setAuthor(newAuthorIdent()); cb.setAuthor(authorIdent);
cb.setCommitter(new PersonIdent(serverIdent, when)); cb.setCommitter(new PersonIdent(serverIdent, when));
if (!curr.equals(z)) { if (!curr.equals(z)) {
cb.setParentId(curr); cb.setParentId(curr);

View File

@ -17,7 +17,6 @@ package com.google.gerrit.server.notedb;
import static com.google.common.base.MoreObjects.firstNonNull; import static com.google.common.base.MoreObjects.firstNonNull;
import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static org.eclipse.jgit.lib.Constants.OBJ_BLOB; import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
import com.google.auto.value.AutoValue; import com.google.auto.value.AutoValue;
@ -28,11 +27,8 @@ import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.client.RefNames; import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.reviewdb.client.RevId; import com.google.gerrit.reviewdb.client.RevId;
import com.google.gerrit.server.GerritPersonIdent; import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.config.AllUsersName; import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.config.AnonymousCowardName; import com.google.gerrit.server.config.AnonymousCowardName;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.project.ChangeControl;
import com.google.gwtorm.server.OrmException; import com.google.gwtorm.server.OrmException;
import com.google.inject.assistedinject.Assisted; import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.AssistedInject; import com.google.inject.assistedinject.AssistedInject;
@ -63,7 +59,8 @@ import java.util.Set;
*/ */
public class ChangeDraftUpdate extends AbstractChangeUpdate { public class ChangeDraftUpdate extends AbstractChangeUpdate {
public interface Factory { public interface Factory {
ChangeDraftUpdate create(ChangeControl ctl, Date when); ChangeDraftUpdate create(ChangeNotes notes, Account.Id accountId,
PersonIdent authorIdent, Date when);
} }
@AutoValue @AutoValue
@ -77,7 +74,6 @@ public class ChangeDraftUpdate extends AbstractChangeUpdate {
} }
private final AllUsersName draftsProject; private final AllUsersName draftsProject;
private final Account.Id accountId;
// TODO: can go back to a list? // TODO: can go back to a list?
private Map<Key, PatchLineComment> put; private Map<Key, PatchLineComment> put;
@ -87,20 +83,16 @@ public class ChangeDraftUpdate extends AbstractChangeUpdate {
private ChangeDraftUpdate( private ChangeDraftUpdate(
@GerritPersonIdent PersonIdent serverIdent, @GerritPersonIdent PersonIdent serverIdent,
@AnonymousCowardName String anonymousCowardName, @AnonymousCowardName String anonymousCowardName,
GitRepositoryManager repoManager,
NotesMigration migration, NotesMigration migration,
AllUsersName allUsers, AllUsersName allUsers,
ChangeNoteUtil noteUtil, ChangeNoteUtil noteUtil,
@Assisted ChangeControl ctl, @Assisted ChangeNotes notes,
@Assisted Account.Id accountId,
@Assisted PersonIdent authorIdent,
@Assisted Date when) { @Assisted Date when) {
super(migration, repoManager, ctl, serverIdent, anonymousCowardName, super(migration, noteUtil, serverIdent, anonymousCowardName, notes,
noteUtil, when); accountId, authorIdent, when);
this.draftsProject = allUsers; this.draftsProject = allUsers;
checkState(ctl.getUser().isIdentifiedUser(),
"Current user must be identified");
IdentifiedUser user = ctl.getUser().asIdentifiedUser();
this.accountId = user.getAccountId();
this.put = new HashMap<>(); this.put = new HashMap<>();
this.delete = new HashSet<>(); this.delete = new HashSet<>();
} }
@ -176,12 +168,12 @@ public class ChangeDraftUpdate extends AbstractChangeUpdate {
// already parsed the revision notes. We can reuse them as long as the ref // already parsed the revision notes. We can reuse them as long as the ref
// hasn't advanced. // hasn't advanced.
DraftCommentNotes draftNotes = DraftCommentNotes draftNotes =
ctl.getNotes().load().getDraftCommentNotes(); getNotes().load().getDraftCommentNotes();
if (draftNotes != null) { if (draftNotes != null) {
ObjectId idFromNotes = ObjectId idFromNotes =
firstNonNull(draftNotes.getRevision(), ObjectId.zeroId()); firstNonNull(draftNotes.getRevision(), ObjectId.zeroId());
if (idFromNotes.equals(curr)) { if (idFromNotes.equals(curr)) {
return checkNotNull(ctl.getNotes().revisionNoteMap); return checkNotNull(getNotes().revisionNoteMap);
} }
} }
} }
@ -194,7 +186,7 @@ public class ChangeDraftUpdate extends AbstractChangeUpdate {
// Even though reading from changes might not be enabled, we need to // Even though reading from changes might not be enabled, we need to
// parse any existing revision notes so we can merge them. // parse any existing revision notes so we can merge them.
return RevisionNoteMap.parse( return RevisionNoteMap.parse(
noteUtil, ctl.getId(), rw.getObjectReader(), noteMap, true); noteUtil, getId(), rw.getObjectReader(), noteMap, true);
} }
@Override @Override
@ -221,7 +213,7 @@ public class ChangeDraftUpdate extends AbstractChangeUpdate {
@Override @Override
protected String getRefName() { protected String getRefName() {
return RefNames.refsDraftComments(accountId, ctl.getId()); return RefNames.refsDraftComments(accountId, getId());
} }
@Override @Override

View File

@ -33,6 +33,7 @@ import com.google.common.collect.Multimap;
import com.google.common.collect.Ordering; import com.google.common.collect.Ordering;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import com.google.common.primitives.Ints; import com.google.common.primitives.Ints;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.reviewdb.client.Account; import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.ChangeMessage; import com.google.gerrit.reviewdb.client.ChangeMessage;
@ -41,24 +42,24 @@ import com.google.gerrit.reviewdb.client.PatchLineComment.Status;
import com.google.gerrit.reviewdb.client.PatchSet; import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.reviewdb.client.PatchSetApproval; import com.google.gerrit.reviewdb.client.PatchSetApproval;
import com.google.gerrit.reviewdb.server.ReviewDb; import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CurrentUser; import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.InternalUser;
import com.google.gerrit.server.PatchLineCommentsUtil; import com.google.gerrit.server.PatchLineCommentsUtil;
import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.config.AnonymousCowardName;
import com.google.gerrit.server.git.ChainedReceiveCommands; import com.google.gerrit.server.git.ChainedReceiveCommands;
import com.google.gerrit.server.patch.PatchListCache; import com.google.gerrit.server.patch.PatchListCache;
import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.project.NoSuchChangeException; import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.schema.DisabledChangesReviewDbWrapper; import com.google.gerrit.server.schema.DisabledChangesReviewDbWrapper;
import com.google.gwtorm.server.OrmException; import com.google.gwtorm.server.OrmException;
import com.google.gwtorm.server.SchemaFactory; import com.google.gwtorm.server.SchemaFactory;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.util.Providers;
import org.eclipse.jgit.errors.ConfigInvalidException; import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.errors.InvalidObjectIdException; import org.eclipse.jgit.errors.InvalidObjectIdException;
import org.eclipse.jgit.errors.MissingObjectException; import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk; import org.eclipse.jgit.revwalk.RevWalk;
@ -68,6 +69,7 @@ import java.io.IOException;
import java.sql.Timestamp; import java.sql.Timestamp;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.Set; import java.util.Set;
@ -92,37 +94,40 @@ public class ChangeRebuilderImpl extends ChangeRebuilder {
*/ */
private static final long MAX_DELTA_MS = SECONDS.toMillis(1); private static final long MAX_DELTA_MS = SECONDS.toMillis(1);
private final ChangeControl.GenericFactory controlFactory; private final AccountCache accountCache;
private final IdentifiedUser.GenericFactory userFactory;
private final InternalUser.Factory internalUserFactory;
private final PatchListCache patchListCache;
private final ChangeNotes.Factory notesFactory;
private final ChangeUpdate.Factory updateFactory;
private final ChangeDraftUpdate.Factory draftUpdateFactory; private final ChangeDraftUpdate.Factory draftUpdateFactory;
private final NoteDbUpdateManager.Factory updateManagerFactory; private final ChangeNotes.Factory notesFactory;
private final ChangeNoteUtil changeNoteUtil; private final ChangeNoteUtil changeNoteUtil;
private final ChangeUpdate.Factory updateFactory;
private final NoteDbUpdateManager.Factory updateManagerFactory;
private final PatchListCache patchListCache;
private final PersonIdent serverIdent;
private final ProjectCache projectCache;
private final String anonymousCowardName;
@Inject @Inject
ChangeRebuilderImpl(SchemaFactory<ReviewDb> schemaFactory, ChangeRebuilderImpl(SchemaFactory<ReviewDb> schemaFactory,
ChangeControl.GenericFactory controlFactory, AccountCache accountCache,
IdentifiedUser.GenericFactory userFactory,
InternalUser.Factory internalUserFactory,
PatchListCache patchListCache,
ChangeNotes.Factory notesFactory,
ChangeUpdate.Factory updateFactory,
ChangeDraftUpdate.Factory draftUpdateFactory, ChangeDraftUpdate.Factory draftUpdateFactory,
ChangeNotes.Factory notesFactory,
ChangeNoteUtil changeNoteUtil,
ChangeUpdate.Factory updateFactory,
NoteDbUpdateManager.Factory updateManagerFactory, NoteDbUpdateManager.Factory updateManagerFactory,
ChangeNoteUtil changeNoteUtil) { PatchListCache patchListCache,
@GerritPersonIdent PersonIdent serverIdent,
@Nullable ProjectCache projectCache,
@AnonymousCowardName String anonymousCowardName) {
super(schemaFactory); super(schemaFactory);
this.controlFactory = controlFactory; this.accountCache = accountCache;
this.userFactory = userFactory;
this.internalUserFactory = internalUserFactory;
this.patchListCache = patchListCache;
this.notesFactory = notesFactory;
this.updateFactory = updateFactory;
this.draftUpdateFactory = draftUpdateFactory; this.draftUpdateFactory = draftUpdateFactory;
this.updateManagerFactory = updateManagerFactory; this.notesFactory = notesFactory;
this.changeNoteUtil = changeNoteUtil; this.changeNoteUtil = changeNoteUtil;
this.updateFactory = updateFactory;
this.updateManagerFactory = updateManagerFactory;
this.patchListCache = patchListCache;
this.serverIdent = serverIdent;
this.projectCache = projectCache;
this.anonymousCowardName = anonymousCowardName;
} }
@Override @Override
@ -174,8 +179,7 @@ public class ChangeRebuilderImpl extends ChangeRebuilder {
} }
private void buildUpdates(NoteDbUpdateManager manager, ChangeBundle bundle) private void buildUpdates(NoteDbUpdateManager manager, ChangeBundle bundle)
throws NoSuchChangeException, IOException, OrmException, throws IOException, OrmException, ConfigInvalidException {
ConfigInvalidException {
Change change = new Change(bundle.getChange()); Change change = new Change(bundle.getChange());
// We will rebuild all events, except for draft comments, in buckets based // We will rebuild all events, except for draft comments, in buckets based
// on author and timestamp. // on author and timestamp.
@ -251,16 +255,25 @@ public class ChangeRebuilderImpl extends ChangeRebuilder {
} }
private void flushEventsToUpdate(NoteDbUpdateManager manager, private void flushEventsToUpdate(NoteDbUpdateManager manager,
EventList<Event> events, Change change) EventList<Event> events, Change change) throws OrmException, IOException {
throws NoSuchChangeException, OrmException, IOException {
if (events.isEmpty()) { if (events.isEmpty()) {
return; return;
} }
Comparator<String> labelNameComparator;
if (projectCache != null) {
labelNameComparator = projectCache.get(change.getProject())
.getLabelTypes().nameComparator();
} else {
// No project cache available, bail and use natural ordering; there's no
// semantic difference anyway difference.
labelNameComparator = Ordering.natural();
}
ChangeUpdate update = updateFactory.create( ChangeUpdate update = updateFactory.create(
controlFactory.controlFor( notesFactory.createWithAutoRebuildingDisabled(change),
notesFactory.createWithAutoRebuildingDisabled(change), events.getAccountId(),
events.getUser()), events.newAuthorIdent(),
events.getWhen()); events.getWhen(),
labelNameComparator);
update.setAllowWriteToNewRef(true); update.setAllowWriteToNewRef(true);
update.setPatchSetId(events.getPatchSetId()); update.setPatchSetId(events.getPatchSetId());
for (Event e : events) { for (Event e : events) {
@ -272,14 +285,14 @@ public class ChangeRebuilderImpl extends ChangeRebuilder {
private void flushEventsToDraftUpdate(NoteDbUpdateManager manager, private void flushEventsToDraftUpdate(NoteDbUpdateManager manager,
EventList<PatchLineCommentEvent> events, Change change) EventList<PatchLineCommentEvent> events, Change change)
throws NoSuchChangeException, OrmException { throws OrmException {
if (events.isEmpty()) { if (events.isEmpty()) {
return; return;
} }
ChangeDraftUpdate update = draftUpdateFactory.create( ChangeDraftUpdate update = draftUpdateFactory.create(
controlFactory.controlFor( notesFactory.createWithAutoRebuildingDisabled(change),
notesFactory.createWithAutoRebuildingDisabled(change), events.getAccountId(),
events.getUser()), events.newAuthorIdent(),
events.getWhen()); events.getWhen());
update.setPatchSetId(events.getPatchSetId()); update.setPatchSetId(events.getPatchSetId());
for (PatchLineCommentEvent e : events) { for (PatchLineCommentEvent e : events) {
@ -389,9 +402,9 @@ public class ChangeRebuilderImpl extends ChangeRebuilder {
checkState(when.getTime() - update.getWhen().getTime() <= MAX_WINDOW_MS, checkState(when.getTime() - update.getWhen().getTime() <= MAX_WINDOW_MS,
"event at %s outside update window starting at %s", "event at %s outside update window starting at %s",
when, update.getWhen()); when, update.getWhen());
checkState(Objects.equals(update.getUser().getAccountId(), who), checkState(Objects.equals(update.getNullableAccountId(), who),
"cannot apply event by %s to update by %s", "cannot apply event by %s to update by %s",
who, update.getUser().getAccountId()); who, update.getNullableAccountId());
} }
/** /**
@ -478,18 +491,23 @@ public class ChangeRebuilderImpl extends ChangeRebuilder {
return id; return id;
} }
CurrentUser getUser() { Account.Id getAccountId() {
Account.Id id = get(0).who; Account.Id id = get(0).who;
for (int i = 1; i < size(); i++) { for (int i = 1; i < size(); i++) {
checkState(Objects.equals(id, get(i).who), checkState(Objects.equals(id, get(i).who),
"mismatched users in EventList: %s != %s", id, get(i).who); "mismatched users in EventList: %s != %s", id, get(i).who);
} }
return id;
}
PersonIdent newAuthorIdent() {
Account.Id id = getAccountId();
if (id == null) { if (id == null) {
return internalUserFactory.create(); return new PersonIdent(serverIdent, getWhen());
} }
// db is only used by IdentifiedUser to look up project watches, which are return changeNoteUtil.newIdent(
// not needed for rebuilding changes. accountCache.get(id).getAccount(), getWhen(), serverIdent,
return userFactory.create(Providers.of((ReviewDb) null), id); anonymousCowardName);
} }
} }

View File

@ -33,26 +33,23 @@ import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_TOPIC;
import static org.eclipse.jgit.lib.Constants.OBJ_BLOB; import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
import com.google.common.base.Joiner; import com.google.common.base.Joiner;
import com.google.common.base.Optional; import com.google.common.base.Optional;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Ordering;
import com.google.common.collect.Table; import com.google.common.collect.Table;
import com.google.common.collect.TreeBasedTable; import com.google.common.collect.TreeBasedTable;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.data.SubmitRecord; import com.google.gerrit.common.data.SubmitRecord;
import com.google.gerrit.reviewdb.client.Account; import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.PatchLineComment; import com.google.gerrit.reviewdb.client.PatchLineComment;
import com.google.gerrit.reviewdb.client.Project; import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.client.RevId; import com.google.gerrit.reviewdb.client.RevId;
import com.google.gerrit.reviewdb.server.ReviewDbUtil;
import com.google.gerrit.server.GerritPersonIdent; import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.account.AccountCache; import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.config.AnonymousCowardName; import com.google.gerrit.server.config.AnonymousCowardName;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.project.ChangeControl; import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.project.ProjectCache; import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.util.LabelVote; import com.google.gerrit.server.util.LabelVote;
@ -71,9 +68,11 @@ import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk; import org.eclipse.jgit.revwalk.RevWalk;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.Comparator; import java.util.Comparator;
import java.util.Date; import java.util.Date;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@ -94,6 +93,10 @@ public class ChangeUpdate extends AbstractChangeUpdate {
public interface Factory { public interface Factory {
ChangeUpdate create(ChangeControl ctl); ChangeUpdate create(ChangeControl ctl);
ChangeUpdate create(ChangeControl ctl, Date when); ChangeUpdate create(ChangeControl ctl, Date when);
ChangeUpdate create(ChangeNotes notes, @Nullable Account.Id accountId,
PersonIdent authorIdent, Date when,
Comparator<String> labelNameComparator);
@VisibleForTesting @VisibleForTesting
ChangeUpdate create(ChangeControl ctl, Date when, ChangeUpdate create(ChangeControl ctl, Date when,
Comparator<String> labelNameComparator); Comparator<String> labelNameComparator);
@ -104,7 +107,8 @@ public class ChangeUpdate extends AbstractChangeUpdate {
private final NoteDbUpdateManager.Factory updateManagerFactory; private final NoteDbUpdateManager.Factory updateManagerFactory;
private final Table<String, Account.Id, Optional<Short>> approvals; private final Table<String, Account.Id, Optional<Short>> approvals;
private final Map<Account.Id, ReviewerStateInternal> reviewers; private final Map<Account.Id, ReviewerStateInternal> reviewers = new LinkedHashMap<>();
private final List<PatchLineComment> comments = new ArrayList<>();
private String commitSubject; private String commitSubject;
private String subject; private String subject;
@ -113,7 +117,6 @@ public class ChangeUpdate extends AbstractChangeUpdate {
private Change.Status status; private Change.Status status;
private List<SubmitRecord> submitRecords; private List<SubmitRecord> submitRecords;
private String submissionId; private String submissionId;
private List<PatchLineComment> comments;
private String topic; private String topic;
private String commit; private String commit;
private Set<String> hashtags; private Set<String> hashtags;
@ -129,7 +132,6 @@ public class ChangeUpdate extends AbstractChangeUpdate {
private ChangeUpdate( private ChangeUpdate(
@GerritPersonIdent PersonIdent serverIdent, @GerritPersonIdent PersonIdent serverIdent,
@AnonymousCowardName String anonymousCowardName, @AnonymousCowardName String anonymousCowardName,
GitRepositoryManager repoManager,
NotesMigration migration, NotesMigration migration,
AccountCache accountCache, AccountCache accountCache,
NoteDbUpdateManager.Factory updateManagerFactory, NoteDbUpdateManager.Factory updateManagerFactory,
@ -137,7 +139,7 @@ public class ChangeUpdate extends AbstractChangeUpdate {
ProjectCache projectCache, ProjectCache projectCache,
@Assisted ChangeControl ctl, @Assisted ChangeControl ctl,
ChangeNoteUtil noteUtil) { ChangeNoteUtil noteUtil) {
this(serverIdent, anonymousCowardName, repoManager, migration, accountCache, this(serverIdent, anonymousCowardName, migration, accountCache,
updateManagerFactory, draftUpdateFactory, updateManagerFactory, draftUpdateFactory,
projectCache, ctl, serverIdent.getWhen(), noteUtil); projectCache, ctl, serverIdent.getWhen(), noteUtil);
} }
@ -146,7 +148,6 @@ public class ChangeUpdate extends AbstractChangeUpdate {
private ChangeUpdate( private ChangeUpdate(
@GerritPersonIdent PersonIdent serverIdent, @GerritPersonIdent PersonIdent serverIdent,
@AnonymousCowardName String anonymousCowardName, @AnonymousCowardName String anonymousCowardName,
GitRepositoryManager repoManager,
NotesMigration migration, NotesMigration migration,
AccountCache accountCache, AccountCache accountCache,
NoteDbUpdateManager.Factory updateManagerFactory, NoteDbUpdateManager.Factory updateManagerFactory,
@ -155,7 +156,7 @@ public class ChangeUpdate extends AbstractChangeUpdate {
@Assisted ChangeControl ctl, @Assisted ChangeControl ctl,
@Assisted Date when, @Assisted Date when,
ChangeNoteUtil noteUtil) { ChangeNoteUtil noteUtil) {
this(serverIdent, anonymousCowardName, repoManager, migration, accountCache, this(serverIdent, anonymousCowardName, migration, accountCache,
updateManagerFactory, draftUpdateFactory, ctl, updateManagerFactory, draftUpdateFactory, ctl,
when, when,
projectCache.get(getProjectName(ctl)).getLabelTypes().nameComparator(), projectCache.get(getProjectName(ctl)).getLabelTypes().nameComparator(),
@ -166,11 +167,15 @@ public class ChangeUpdate extends AbstractChangeUpdate {
return ctl.getProject().getNameKey(); return ctl.getProject().getNameKey();
} }
private static Table<String, Account.Id, Optional<Short>> approvals(
Comparator<String> nameComparator) {
return TreeBasedTable.create(nameComparator, ReviewDbUtil.intKeyOrdering());
}
@AssistedInject @AssistedInject
private ChangeUpdate( private ChangeUpdate(
@GerritPersonIdent PersonIdent serverIdent, @GerritPersonIdent PersonIdent serverIdent,
@AnonymousCowardName String anonymousCowardName, @AnonymousCowardName String anonymousCowardName,
GitRepositoryManager repoManager,
NotesMigration migration, NotesMigration migration,
AccountCache accountCache, AccountCache accountCache,
NoteDbUpdateManager.Factory updateManagerFactory, NoteDbUpdateManager.Factory updateManagerFactory,
@ -179,22 +184,34 @@ public class ChangeUpdate extends AbstractChangeUpdate {
@Assisted Date when, @Assisted Date when,
@Assisted Comparator<String> labelNameComparator, @Assisted Comparator<String> labelNameComparator,
ChangeNoteUtil noteUtil) { ChangeNoteUtil noteUtil) {
super(migration, repoManager, ctl, serverIdent, super(migration, ctl, serverIdent,
anonymousCowardName, noteUtil, when); anonymousCowardName, noteUtil, when);
this.accountCache = accountCache; this.accountCache = accountCache;
this.draftUpdateFactory = draftUpdateFactory; this.draftUpdateFactory = draftUpdateFactory;
this.updateManagerFactory = updateManagerFactory; this.updateManagerFactory = updateManagerFactory;
this.approvals = approvals(labelNameComparator);
}
this.approvals = TreeBasedTable.create( @AssistedInject
labelNameComparator, private ChangeUpdate(
Ordering.natural().onResultOf(new Function<Account.Id, Integer>() { @GerritPersonIdent PersonIdent serverIdent,
@Override @AnonymousCowardName String anonymousCowardName,
public Integer apply(Account.Id in) { NotesMigration migration,
return in.get(); AccountCache accountCache,
} NoteDbUpdateManager.Factory updateManagerFactory,
})); ChangeDraftUpdate.Factory draftUpdateFactory,
this.reviewers = Maps.newLinkedHashMap(); ChangeNoteUtil noteUtil,
this.comments = Lists.newArrayList(); @Assisted ChangeNotes notes,
@Assisted @Nullable Account.Id accountId,
@Assisted PersonIdent authorIdent,
@Assisted Date when,
@Assisted Comparator<String> labelNameComparator) {
super(migration, noteUtil, serverIdent, anonymousCowardName, notes,
accountId, authorIdent, when);
this.accountCache = accountCache;
this.draftUpdateFactory = draftUpdateFactory;
this.updateManagerFactory = updateManagerFactory;
this.approvals = approvals(labelNameComparator);
} }
public ObjectId commit() throws IOException, OrmException { public ObjectId commit() throws IOException, OrmException {
@ -203,13 +220,13 @@ public class ChangeUpdate extends AbstractChangeUpdate {
updateManager.add(this); updateManager.add(this);
NoteDbChangeState.applyDelta( NoteDbChangeState.applyDelta(
getChange(), getChange(),
updateManager.stage().get(ctl.getId())); updateManager.stage().get(getId()));
updateManager.execute(); updateManager.execute();
return getResult(); return getResult();
} }
public void setChangeId(String changeId) { public void setChangeId(String changeId) {
String old = ctl.getChange().getKey().get(); String old = getChange().getKey().get();
checkArgument(old.equals(changeId), checkArgument(old.equals(changeId),
"The Change-Id was already set to %s, so we cannot set this Change-Id: %s", "The Change-Id was already set to %s, so we cannot set this Change-Id: %s",
old, changeId); old, changeId);
@ -231,7 +248,7 @@ public class ChangeUpdate extends AbstractChangeUpdate {
} }
public void putApproval(String label, short value) { public void putApproval(String label, short value) {
putApprovalFor(getUser().getAccountId(), label, value); putApprovalFor(getAccountId(), label, value);
} }
public void putApprovalFor(Account.Id reviewer, String label, short value) { public void putApprovalFor(Account.Id reviewer, String label, short value) {
@ -239,7 +256,7 @@ public class ChangeUpdate extends AbstractChangeUpdate {
} }
public void removeApproval(String label) { public void removeApproval(String label) {
removeApprovalFor(getUser().getAccountId(), label); removeApprovalFor(getAccountId(), label);
} }
public void removeApprovalFor(Account.Id reviewer, String label) { public void removeApprovalFor(Account.Id reviewer, String label) {
@ -304,16 +321,17 @@ public class ChangeUpdate extends AbstractChangeUpdate {
@VisibleForTesting @VisibleForTesting
ChangeDraftUpdate createDraftUpdateIfNull() { ChangeDraftUpdate createDraftUpdateIfNull() {
if (draftUpdate == null) { if (draftUpdate == null) {
draftUpdate = draftUpdateFactory.create(ctl, when); draftUpdate =
draftUpdateFactory.create(getNotes(), accountId, authorIdent, when);
} }
return draftUpdate; return draftUpdate;
} }
private void verifyComment(PatchLineComment c) { private void verifyComment(PatchLineComment c) {
checkArgument(c.getRevId() != null, "RevId required for comment: %s", c); checkArgument(c.getRevId() != null, "RevId required for comment: %s", c);
checkArgument(c.getAuthor().equals(getUser().getAccountId()), checkArgument(c.getAuthor().equals(getAccountId()),
"The author for the following comment does not match the author of" "The author for the following comment does not match the author of"
+ " this ChangeDraftUpdate (%s): %s", getUser().getAccountId(), c); + " this ChangeDraftUpdate (%s): %s", getAccountId(), c);
} }
@ -403,16 +421,16 @@ public class ChangeUpdate extends AbstractChangeUpdate {
// parsed the revision notes. We can reuse them as long as the ref hasn't // parsed the revision notes. We can reuse them as long as the ref hasn't
// advanced. // advanced.
ObjectId idFromNotes = ObjectId idFromNotes =
firstNonNull(ctl.getNotes().load().getRevision(), ObjectId.zeroId()); firstNonNull(getNotes().load().getRevision(), ObjectId.zeroId());
if (idFromNotes.equals(curr)) { if (idFromNotes.equals(curr)) {
return checkNotNull(ctl.getNotes().revisionNoteMap); return checkNotNull(getNotes().revisionNoteMap);
} }
} }
NoteMap noteMap = NoteMap.read(rw.getObjectReader(), rw.parseCommit(curr)); NoteMap noteMap = NoteMap.read(rw.getObjectReader(), rw.parseCommit(curr));
// Even though reading from changes might not be enabled, we need to // Even though reading from changes might not be enabled, we need to
// parse any existing revision notes so we can merge them. // parse any existing revision notes so we can merge them.
return RevisionNoteMap.parse( return RevisionNoteMap.parse(
noteUtil, ctl.getId(), rw.getObjectReader(), noteMap, false); noteUtil, getId(), rw.getObjectReader(), noteMap, false);
} }
private void checkComments(Map<RevId, RevisionNote> existingNotes, private void checkComments(Map<RevId, RevisionNote> existingNotes,
@ -455,7 +473,7 @@ public class ChangeUpdate extends AbstractChangeUpdate {
@Override @Override
protected String getRefName() { protected String getRefName() {
return ChangeNoteUtil.changeRefName(ctl.getId()); return ChangeNoteUtil.changeRefName(getId());
} }
@Override @Override
@ -527,7 +545,7 @@ public class ChangeUpdate extends AbstractChangeUpdate {
c.getRowKey(), c.getValue().get()).formatWithEquals()); c.getRowKey(), c.getValue().get()).formatWithEquals());
} }
Account.Id id = c.getColumnKey(); Account.Id id = c.getColumnKey();
if (!id.equals(ctl.getUser().getAccountId())) { if (!id.equals(getAccountId())) {
addIdent(msg.append(' '), id); addIdent(msg.append(' '), id);
} }
msg.append('\n'); msg.append('\n');
@ -584,7 +602,7 @@ public class ChangeUpdate extends AbstractChangeUpdate {
@Override @Override
protected Project.NameKey getProjectName() { protected Project.NameKey getProjectName() {
return getProjectName(ctl); return getChange().getProject();
} }
@Override @Override

View File

@ -207,6 +207,7 @@ public abstract class AbstractChangeNotesTest extends GerritBaseTests {
protected ChangeUpdate newUpdate(Change c, CurrentUser user) protected ChangeUpdate newUpdate(Change c, CurrentUser user)
throws Exception { throws Exception {
ChangeUpdate update = TestChanges.newUpdate(injector, c, user); ChangeUpdate update = TestChanges.newUpdate(injector, c, user);
update.setPatchSetId(c.currentPatchSetId());
update.setAllowWriteToNewRef(true); update.setAllowWriteToNewRef(true);
return update; return update;
} }

View File

@ -1760,7 +1760,7 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
assertThat(msg.getAuthor()).isNull(); assertThat(msg.getAuthor()).isNull();
update = newUpdate(c, internalUser); update = newUpdate(c, internalUser);
exception.expect(UnsupportedOperationException.class); exception.expect(IllegalStateException.class);
update.putApproval("Code-Review", (short) 1); update.putApproval("Code-Review", (short) 1);
} }

View File

@ -101,7 +101,7 @@ public class TestChanges {
user), user),
TimeUtil.nowTs(), Ordering.<String> natural()); TimeUtil.nowTs(), Ordering.<String> natural());
ChangeNotes notes = update.getChangeNotes(); ChangeNotes notes = update.getNotes();
boolean hasPatchSets = notes.getPatchSets() != null boolean hasPatchSets = notes.getPatchSets() != null
&& !notes.getPatchSets().isEmpty(); && !notes.getPatchSets().isEmpty();
NotesMigration migration = injector.getInstance(NotesMigration.class); NotesMigration migration = injector.getInstance(NotesMigration.class);