Remove the "get" prefix on AutoValue getters. Also remove the "is" in "isPostSubmit", which AutoValue treats the same as "get"; note how the method on the Builder class was already required to be named postSubmit. Change-Id: Ic2f6fe9b7195bcec9b021d65eb3ca6c9f74f0c45
670 lines
25 KiB
Java
670 lines
25 KiB
Java
// Copyright (C) 2016 The Android Open Source Project
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package com.google.gerrit.server.notedb;
|
|
|
|
import static com.google.common.base.Preconditions.checkArgument;
|
|
import static com.google.common.base.Preconditions.checkState;
|
|
import static com.google.common.collect.ImmutableList.toImmutableList;
|
|
import static com.google.common.collect.ImmutableListMultimap.toImmutableListMultimap;
|
|
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
|
import static java.util.Objects.requireNonNull;
|
|
|
|
import com.google.auto.value.AutoValue;
|
|
import com.google.common.annotations.VisibleForTesting;
|
|
import com.google.common.base.Converter;
|
|
import com.google.common.base.Enums;
|
|
import com.google.common.base.Strings;
|
|
import com.google.common.collect.ImmutableList;
|
|
import com.google.common.collect.ImmutableListMultimap;
|
|
import com.google.common.collect.ImmutableSet;
|
|
import com.google.common.collect.ImmutableTable;
|
|
import com.google.common.collect.ListMultimap;
|
|
import com.google.common.collect.Maps;
|
|
import com.google.common.collect.Table;
|
|
import com.google.gerrit.common.Nullable;
|
|
import com.google.gerrit.common.data.SubmitRecord;
|
|
import com.google.gerrit.json.OutputFormat;
|
|
import com.google.gerrit.mail.Address;
|
|
import com.google.gerrit.proto.Protos;
|
|
import com.google.gerrit.reviewdb.client.Account;
|
|
import com.google.gerrit.reviewdb.client.BranchNameKey;
|
|
import com.google.gerrit.reviewdb.client.Change;
|
|
import com.google.gerrit.reviewdb.client.ChangeMessage;
|
|
import com.google.gerrit.reviewdb.client.Comment;
|
|
import com.google.gerrit.reviewdb.client.PatchSet;
|
|
import com.google.gerrit.reviewdb.client.PatchSetApproval;
|
|
import com.google.gerrit.reviewdb.client.Project;
|
|
import com.google.gerrit.reviewdb.converter.ChangeMessageProtoConverter;
|
|
import com.google.gerrit.reviewdb.converter.PatchSetApprovalProtoConverter;
|
|
import com.google.gerrit.reviewdb.converter.PatchSetProtoConverter;
|
|
import com.google.gerrit.reviewdb.converter.ProtoConverter;
|
|
import com.google.gerrit.server.ReviewerByEmailSet;
|
|
import com.google.gerrit.server.ReviewerSet;
|
|
import com.google.gerrit.server.ReviewerStatusUpdate;
|
|
import com.google.gerrit.server.cache.proto.Cache.ChangeNotesStateProto;
|
|
import com.google.gerrit.server.cache.proto.Cache.ChangeNotesStateProto.ChangeColumnsProto;
|
|
import com.google.gerrit.server.cache.proto.Cache.ChangeNotesStateProto.ReviewerByEmailSetEntryProto;
|
|
import com.google.gerrit.server.cache.proto.Cache.ChangeNotesStateProto.ReviewerSetEntryProto;
|
|
import com.google.gerrit.server.cache.proto.Cache.ChangeNotesStateProto.ReviewerStatusUpdateProto;
|
|
import com.google.gerrit.server.cache.serialize.CacheSerializer;
|
|
import com.google.gerrit.server.cache.serialize.ObjectIdConverter;
|
|
import com.google.gerrit.server.index.change.ChangeField.StoredSubmitRecord;
|
|
import com.google.gson.Gson;
|
|
import com.google.protobuf.ByteString;
|
|
import com.google.protobuf.MessageLite;
|
|
import java.sql.Timestamp;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Set;
|
|
import org.eclipse.jgit.lib.ObjectId;
|
|
|
|
/**
|
|
* Immutable state associated with a change meta ref at a given commit.
|
|
*
|
|
* <p>One instance is the output of a single {@link ChangeNotesParser}, and contains types required
|
|
* to support public methods on {@link ChangeNotes}. It is intended to be cached in-process.
|
|
*
|
|
* <p>Note that {@link ChangeNotes} contains more than just a single {@code ChangeNoteState}, such
|
|
* as per-draft information, so that class is not cached directly.
|
|
*/
|
|
@AutoValue
|
|
public abstract class ChangeNotesState {
|
|
static ChangeNotesState empty(Change change) {
|
|
return Builder.empty(change.getId()).build();
|
|
}
|
|
|
|
static Builder builder() {
|
|
return new AutoValue_ChangeNotesState.Builder();
|
|
}
|
|
|
|
static ChangeNotesState create(
|
|
ObjectId metaId,
|
|
Change.Id changeId,
|
|
Change.Key changeKey,
|
|
Timestamp createdOn,
|
|
Timestamp lastUpdatedOn,
|
|
Account.Id owner,
|
|
String branch,
|
|
@Nullable PatchSet.Id currentPatchSetId,
|
|
String subject,
|
|
@Nullable String topic,
|
|
@Nullable String originalSubject,
|
|
@Nullable String submissionId,
|
|
@Nullable Account.Id assignee,
|
|
@Nullable Change.Status status,
|
|
Set<Account.Id> pastAssignees,
|
|
Set<String> hashtags,
|
|
Map<PatchSet.Id, PatchSet> patchSets,
|
|
ListMultimap<PatchSet.Id, PatchSetApproval> approvals,
|
|
ReviewerSet reviewers,
|
|
ReviewerByEmailSet reviewersByEmail,
|
|
ReviewerSet pendingReviewers,
|
|
ReviewerByEmailSet pendingReviewersByEmail,
|
|
List<Account.Id> allPastReviewers,
|
|
List<ReviewerStatusUpdate> reviewerUpdates,
|
|
List<SubmitRecord> submitRecords,
|
|
List<ChangeMessage> changeMessages,
|
|
ListMultimap<ObjectId, Comment> publishedComments,
|
|
boolean isPrivate,
|
|
boolean workInProgress,
|
|
boolean reviewStarted,
|
|
@Nullable Change.Id revertOf,
|
|
int updateCount) {
|
|
requireNonNull(
|
|
metaId,
|
|
() ->
|
|
String.format(
|
|
"metaId is required when passing arguments to create(...)."
|
|
+ " To create an empty %s without"
|
|
+ " NoteDb data, use empty(...) instead",
|
|
ChangeNotesState.class.getSimpleName()));
|
|
return builder()
|
|
.metaId(metaId)
|
|
.changeId(changeId)
|
|
.columns(
|
|
ChangeColumns.builder()
|
|
.changeKey(changeKey)
|
|
.createdOn(createdOn)
|
|
.lastUpdatedOn(lastUpdatedOn)
|
|
.owner(owner)
|
|
.branch(branch)
|
|
.status(status)
|
|
.currentPatchSetId(currentPatchSetId)
|
|
.subject(subject)
|
|
.topic(topic)
|
|
.originalSubject(originalSubject)
|
|
.submissionId(submissionId)
|
|
.assignee(assignee)
|
|
.isPrivate(isPrivate)
|
|
.workInProgress(workInProgress)
|
|
.reviewStarted(reviewStarted)
|
|
.revertOf(revertOf)
|
|
.build())
|
|
.pastAssignees(pastAssignees)
|
|
.hashtags(hashtags)
|
|
.patchSets(patchSets.entrySet())
|
|
.approvals(approvals.entries())
|
|
.reviewers(reviewers)
|
|
.reviewersByEmail(reviewersByEmail)
|
|
.pendingReviewers(pendingReviewers)
|
|
.pendingReviewersByEmail(pendingReviewersByEmail)
|
|
.allPastReviewers(allPastReviewers)
|
|
.reviewerUpdates(reviewerUpdates)
|
|
.submitRecords(submitRecords)
|
|
.changeMessages(changeMessages)
|
|
.publishedComments(publishedComments)
|
|
.updateCount(updateCount)
|
|
.build();
|
|
}
|
|
|
|
/**
|
|
* Subset of Change columns that can be represented in NoteDb.
|
|
*
|
|
* <p>Fields should match the column names in {@link Change}, and are in listed column order.
|
|
*/
|
|
@AutoValue
|
|
abstract static class ChangeColumns {
|
|
static Builder builder() {
|
|
return new AutoValue_ChangeNotesState_ChangeColumns.Builder();
|
|
}
|
|
|
|
abstract Change.Key changeKey();
|
|
|
|
abstract Timestamp createdOn();
|
|
|
|
abstract Timestamp lastUpdatedOn();
|
|
|
|
abstract Account.Id owner();
|
|
|
|
// Project not included, as it's not stored anywhere in the meta ref.
|
|
abstract String branch();
|
|
|
|
// TODO(dborowitz): Use a sensible default other than null
|
|
@Nullable
|
|
abstract Change.Status status();
|
|
|
|
@Nullable
|
|
abstract PatchSet.Id currentPatchSetId();
|
|
|
|
abstract String subject();
|
|
|
|
@Nullable
|
|
abstract String topic();
|
|
|
|
@Nullable
|
|
abstract String originalSubject();
|
|
|
|
@Nullable
|
|
abstract String submissionId();
|
|
|
|
@Nullable
|
|
abstract Account.Id assignee();
|
|
|
|
abstract boolean isPrivate();
|
|
|
|
abstract boolean workInProgress();
|
|
|
|
abstract boolean reviewStarted();
|
|
|
|
@Nullable
|
|
abstract Change.Id revertOf();
|
|
|
|
abstract Builder toBuilder();
|
|
|
|
@AutoValue.Builder
|
|
abstract static class Builder {
|
|
abstract Builder changeKey(Change.Key changeKey);
|
|
|
|
abstract Builder createdOn(Timestamp createdOn);
|
|
|
|
abstract Builder lastUpdatedOn(Timestamp lastUpdatedOn);
|
|
|
|
abstract Builder owner(Account.Id owner);
|
|
|
|
abstract Builder branch(String branch);
|
|
|
|
abstract Builder currentPatchSetId(@Nullable PatchSet.Id currentPatchSetId);
|
|
|
|
abstract Builder subject(String subject);
|
|
|
|
abstract Builder topic(@Nullable String topic);
|
|
|
|
abstract Builder originalSubject(@Nullable String originalSubject);
|
|
|
|
abstract Builder submissionId(@Nullable String submissionId);
|
|
|
|
abstract Builder assignee(@Nullable Account.Id assignee);
|
|
|
|
abstract Builder status(@Nullable Change.Status status);
|
|
|
|
abstract Builder isPrivate(boolean isPrivate);
|
|
|
|
abstract Builder workInProgress(boolean workInProgress);
|
|
|
|
abstract Builder reviewStarted(boolean reviewStarted);
|
|
|
|
abstract Builder revertOf(@Nullable Change.Id revertOf);
|
|
|
|
abstract ChangeColumns build();
|
|
}
|
|
}
|
|
|
|
// Only null if NoteDb is disabled.
|
|
@Nullable
|
|
abstract ObjectId metaId();
|
|
|
|
abstract Change.Id changeId();
|
|
|
|
// Only null if NoteDb is disabled.
|
|
@Nullable
|
|
abstract ChangeColumns columns();
|
|
|
|
// Other related to this Change.
|
|
abstract ImmutableSet<Account.Id> pastAssignees();
|
|
|
|
abstract ImmutableSet<String> hashtags();
|
|
|
|
abstract ImmutableList<Map.Entry<PatchSet.Id, PatchSet>> patchSets();
|
|
|
|
abstract ImmutableList<Map.Entry<PatchSet.Id, PatchSetApproval>> approvals();
|
|
|
|
abstract ReviewerSet reviewers();
|
|
|
|
abstract ReviewerByEmailSet reviewersByEmail();
|
|
|
|
abstract ReviewerSet pendingReviewers();
|
|
|
|
abstract ReviewerByEmailSet pendingReviewersByEmail();
|
|
|
|
abstract ImmutableList<Account.Id> allPastReviewers();
|
|
|
|
abstract ImmutableList<ReviewerStatusUpdate> reviewerUpdates();
|
|
|
|
abstract ImmutableList<SubmitRecord> submitRecords();
|
|
|
|
abstract ImmutableList<ChangeMessage> changeMessages();
|
|
|
|
abstract ImmutableListMultimap<ObjectId, Comment> publishedComments();
|
|
|
|
abstract int updateCount();
|
|
|
|
Change newChange(Project.NameKey project) {
|
|
ChangeColumns c = requireNonNull(columns(), "columns are required");
|
|
Change change =
|
|
new Change(
|
|
c.changeKey(),
|
|
changeId(),
|
|
c.owner(),
|
|
BranchNameKey.create(project, c.branch()),
|
|
c.createdOn());
|
|
copyNonConstructorColumnsTo(change);
|
|
return change;
|
|
}
|
|
|
|
void copyColumnsTo(Change change) {
|
|
ChangeColumns c = columns();
|
|
checkState(
|
|
c != null && metaId() != null,
|
|
"missing columns or metaId in ChangeNotesState; is NoteDb enabled? %s",
|
|
this);
|
|
change.setKey(c.changeKey());
|
|
change.setOwner(c.owner());
|
|
change.setDest(BranchNameKey.create(change.getProject(), c.branch()));
|
|
change.setCreatedOn(c.createdOn());
|
|
copyNonConstructorColumnsTo(change);
|
|
}
|
|
|
|
private void copyNonConstructorColumnsTo(Change change) {
|
|
ChangeColumns c = requireNonNull(columns(), "columns are required");
|
|
if (c.status() != null) {
|
|
change.setStatus(c.status());
|
|
}
|
|
change.setTopic(Strings.emptyToNull(c.topic()));
|
|
change.setLastUpdatedOn(c.lastUpdatedOn());
|
|
change.setSubmissionId(c.submissionId());
|
|
change.setAssignee(c.assignee());
|
|
change.setPrivate(c.isPrivate());
|
|
change.setWorkInProgress(c.workInProgress());
|
|
change.setReviewStarted(c.reviewStarted());
|
|
change.setRevertOf(c.revertOf());
|
|
|
|
if (!patchSets().isEmpty()) {
|
|
change.setCurrentPatchSet(c.currentPatchSetId(), c.subject(), c.originalSubject());
|
|
} else {
|
|
// TODO(dborowitz): This should be an error, but for now it's required for
|
|
// some tests to pass.
|
|
change.clearCurrentPatchSet();
|
|
}
|
|
}
|
|
|
|
@AutoValue.Builder
|
|
abstract static class Builder {
|
|
static Builder empty(Change.Id changeId) {
|
|
return new AutoValue_ChangeNotesState.Builder()
|
|
.changeId(changeId)
|
|
.pastAssignees(ImmutableSet.of())
|
|
.hashtags(ImmutableSet.of())
|
|
.patchSets(ImmutableList.of())
|
|
.approvals(ImmutableList.of())
|
|
.reviewers(ReviewerSet.empty())
|
|
.reviewersByEmail(ReviewerByEmailSet.empty())
|
|
.pendingReviewers(ReviewerSet.empty())
|
|
.pendingReviewersByEmail(ReviewerByEmailSet.empty())
|
|
.allPastReviewers(ImmutableList.of())
|
|
.reviewerUpdates(ImmutableList.of())
|
|
.submitRecords(ImmutableList.of())
|
|
.changeMessages(ImmutableList.of())
|
|
.publishedComments(ImmutableListMultimap.of())
|
|
.updateCount(0);
|
|
}
|
|
|
|
abstract Builder metaId(ObjectId metaId);
|
|
|
|
abstract Builder changeId(Change.Id changeId);
|
|
|
|
abstract Builder columns(ChangeColumns columns);
|
|
|
|
abstract Builder pastAssignees(Set<Account.Id> pastAssignees);
|
|
|
|
abstract Builder hashtags(Iterable<String> hashtags);
|
|
|
|
abstract Builder patchSets(Iterable<Map.Entry<PatchSet.Id, PatchSet>> patchSets);
|
|
|
|
abstract Builder approvals(Iterable<Map.Entry<PatchSet.Id, PatchSetApproval>> approvals);
|
|
|
|
abstract Builder reviewers(ReviewerSet reviewers);
|
|
|
|
abstract Builder reviewersByEmail(ReviewerByEmailSet reviewersByEmail);
|
|
|
|
abstract Builder pendingReviewers(ReviewerSet pendingReviewers);
|
|
|
|
abstract Builder pendingReviewersByEmail(ReviewerByEmailSet pendingReviewersByEmail);
|
|
|
|
abstract Builder allPastReviewers(List<Account.Id> allPastReviewers);
|
|
|
|
abstract Builder reviewerUpdates(List<ReviewerStatusUpdate> reviewerUpdates);
|
|
|
|
abstract Builder submitRecords(List<SubmitRecord> submitRecords);
|
|
|
|
abstract Builder changeMessages(List<ChangeMessage> changeMessages);
|
|
|
|
abstract Builder publishedComments(ListMultimap<ObjectId, Comment> publishedComments);
|
|
|
|
abstract Builder updateCount(int updateCount);
|
|
|
|
abstract ChangeNotesState build();
|
|
}
|
|
|
|
enum Serializer implements CacheSerializer<ChangeNotesState> {
|
|
INSTANCE;
|
|
|
|
@VisibleForTesting static final Gson GSON = OutputFormat.JSON_COMPACT.newGson();
|
|
|
|
private static final Converter<String, Change.Status> STATUS_CONVERTER =
|
|
Enums.stringConverter(Change.Status.class);
|
|
private static final Converter<String, ReviewerStateInternal> REVIEWER_STATE_CONVERTER =
|
|
Enums.stringConverter(ReviewerStateInternal.class);
|
|
|
|
@Override
|
|
public byte[] serialize(ChangeNotesState object) {
|
|
checkArgument(object.metaId() != null, "meta ID is required in: %s", object);
|
|
checkArgument(object.columns() != null, "ChangeColumns is required in: %s", object);
|
|
ChangeNotesStateProto.Builder b = ChangeNotesStateProto.newBuilder();
|
|
|
|
b.setMetaId(ObjectIdConverter.create().toByteString(object.metaId()))
|
|
.setChangeId(object.changeId().get())
|
|
.setColumns(toChangeColumnsProto(object.columns()));
|
|
|
|
object.pastAssignees().forEach(a -> b.addPastAssignee(a.get()));
|
|
object.hashtags().forEach(b::addHashtag);
|
|
object
|
|
.patchSets()
|
|
.forEach(e -> b.addPatchSet(toByteString(e.getValue(), PatchSetProtoConverter.INSTANCE)));
|
|
object
|
|
.approvals()
|
|
.forEach(
|
|
e ->
|
|
b.addApproval(
|
|
toByteString(e.getValue(), PatchSetApprovalProtoConverter.INSTANCE)));
|
|
|
|
object.reviewers().asTable().cellSet().forEach(c -> b.addReviewer(toReviewerSetEntry(c)));
|
|
object
|
|
.reviewersByEmail()
|
|
.asTable()
|
|
.cellSet()
|
|
.forEach(c -> b.addReviewerByEmail(toReviewerByEmailSetEntry(c)));
|
|
object
|
|
.pendingReviewers()
|
|
.asTable()
|
|
.cellSet()
|
|
.forEach(c -> b.addPendingReviewer(toReviewerSetEntry(c)));
|
|
object
|
|
.pendingReviewersByEmail()
|
|
.asTable()
|
|
.cellSet()
|
|
.forEach(c -> b.addPendingReviewerByEmail(toReviewerByEmailSetEntry(c)));
|
|
|
|
object.allPastReviewers().forEach(a -> b.addPastReviewer(a.get()));
|
|
object.reviewerUpdates().forEach(u -> b.addReviewerUpdate(toReviewerStatusUpdateProto(u)));
|
|
object
|
|
.submitRecords()
|
|
.forEach(r -> b.addSubmitRecord(GSON.toJson(new StoredSubmitRecord(r))));
|
|
object
|
|
.changeMessages()
|
|
.forEach(m -> b.addChangeMessage(toByteString(m, ChangeMessageProtoConverter.INSTANCE)));
|
|
object.publishedComments().values().forEach(c -> b.addPublishedComment(GSON.toJson(c)));
|
|
b.setUpdateCount(object.updateCount());
|
|
|
|
return Protos.toByteArray(b.build());
|
|
}
|
|
|
|
@VisibleForTesting
|
|
static <T> ByteString toByteString(T object, ProtoConverter<?, T> converter) {
|
|
MessageLite message = converter.toProto(object);
|
|
return Protos.toByteString(message);
|
|
}
|
|
|
|
private static ChangeColumnsProto toChangeColumnsProto(ChangeColumns cols) {
|
|
ChangeColumnsProto.Builder b =
|
|
ChangeColumnsProto.newBuilder()
|
|
.setChangeKey(cols.changeKey().get())
|
|
.setCreatedOn(cols.createdOn().getTime())
|
|
.setLastUpdatedOn(cols.lastUpdatedOn().getTime())
|
|
.setOwner(cols.owner().get())
|
|
.setBranch(cols.branch());
|
|
if (cols.currentPatchSetId() != null) {
|
|
b.setCurrentPatchSetId(cols.currentPatchSetId().get()).setHasCurrentPatchSetId(true);
|
|
}
|
|
b.setSubject(cols.subject());
|
|
if (cols.topic() != null) {
|
|
b.setTopic(cols.topic()).setHasTopic(true);
|
|
}
|
|
if (cols.originalSubject() != null) {
|
|
b.setOriginalSubject(cols.originalSubject()).setHasOriginalSubject(true);
|
|
}
|
|
if (cols.submissionId() != null) {
|
|
b.setSubmissionId(cols.submissionId()).setHasSubmissionId(true);
|
|
}
|
|
if (cols.assignee() != null) {
|
|
b.setAssignee(cols.assignee().get()).setHasAssignee(true);
|
|
}
|
|
if (cols.status() != null) {
|
|
b.setStatus(STATUS_CONVERTER.reverse().convert(cols.status())).setHasStatus(true);
|
|
}
|
|
b.setIsPrivate(cols.isPrivate())
|
|
.setWorkInProgress(cols.workInProgress())
|
|
.setReviewStarted(cols.reviewStarted());
|
|
if (cols.revertOf() != null) {
|
|
b.setRevertOf(cols.revertOf().get()).setHasRevertOf(true);
|
|
}
|
|
return b.build();
|
|
}
|
|
|
|
private static ReviewerSetEntryProto toReviewerSetEntry(
|
|
Table.Cell<ReviewerStateInternal, Account.Id, Timestamp> c) {
|
|
return ReviewerSetEntryProto.newBuilder()
|
|
.setState(REVIEWER_STATE_CONVERTER.reverse().convert(c.getRowKey()))
|
|
.setAccountId(c.getColumnKey().get())
|
|
.setTimestamp(c.getValue().getTime())
|
|
.build();
|
|
}
|
|
|
|
private static ReviewerByEmailSetEntryProto toReviewerByEmailSetEntry(
|
|
Table.Cell<ReviewerStateInternal, Address, Timestamp> c) {
|
|
return ReviewerByEmailSetEntryProto.newBuilder()
|
|
.setState(REVIEWER_STATE_CONVERTER.reverse().convert(c.getRowKey()))
|
|
.setAddress(c.getColumnKey().toHeaderString())
|
|
.setTimestamp(c.getValue().getTime())
|
|
.build();
|
|
}
|
|
|
|
private static ReviewerStatusUpdateProto toReviewerStatusUpdateProto(ReviewerStatusUpdate u) {
|
|
return ReviewerStatusUpdateProto.newBuilder()
|
|
.setDate(u.date().getTime())
|
|
.setUpdatedBy(u.updatedBy().get())
|
|
.setReviewer(u.reviewer().get())
|
|
.setState(REVIEWER_STATE_CONVERTER.reverse().convert(u.state()))
|
|
.build();
|
|
}
|
|
|
|
@Override
|
|
public ChangeNotesState deserialize(byte[] in) {
|
|
ChangeNotesStateProto proto = Protos.parseUnchecked(ChangeNotesStateProto.parser(), in);
|
|
Change.Id changeId = Change.id(proto.getChangeId());
|
|
|
|
ChangeNotesState.Builder b =
|
|
builder()
|
|
.metaId(ObjectIdConverter.create().fromByteString(proto.getMetaId()))
|
|
.changeId(changeId)
|
|
.columns(toChangeColumns(changeId, proto.getColumns()))
|
|
.pastAssignees(
|
|
proto.getPastAssigneeList().stream().map(Account::id).collect(toImmutableSet()))
|
|
.hashtags(proto.getHashtagList())
|
|
.patchSets(
|
|
proto.getPatchSetList().stream()
|
|
.map(bytes -> parseProtoFrom(PatchSetProtoConverter.INSTANCE, bytes))
|
|
.map(ps -> Maps.immutableEntry(ps.id(), ps))
|
|
.collect(toImmutableList()))
|
|
.approvals(
|
|
proto.getApprovalList().stream()
|
|
.map(bytes -> parseProtoFrom(PatchSetApprovalProtoConverter.INSTANCE, bytes))
|
|
.map(a -> Maps.immutableEntry(a.patchSetId(), a))
|
|
.collect(toImmutableList()))
|
|
.reviewers(toReviewerSet(proto.getReviewerList()))
|
|
.reviewersByEmail(toReviewerByEmailSet(proto.getReviewerByEmailList()))
|
|
.pendingReviewers(toReviewerSet(proto.getPendingReviewerList()))
|
|
.pendingReviewersByEmail(toReviewerByEmailSet(proto.getPendingReviewerByEmailList()))
|
|
.allPastReviewers(
|
|
proto.getPastReviewerList().stream().map(Account::id).collect(toImmutableList()))
|
|
.reviewerUpdates(toReviewerStatusUpdateList(proto.getReviewerUpdateList()))
|
|
.submitRecords(
|
|
proto.getSubmitRecordList().stream()
|
|
.map(r -> GSON.fromJson(r, StoredSubmitRecord.class).toSubmitRecord())
|
|
.collect(toImmutableList()))
|
|
.changeMessages(
|
|
proto.getChangeMessageList().stream()
|
|
.map(bytes -> parseProtoFrom(ChangeMessageProtoConverter.INSTANCE, bytes))
|
|
.collect(toImmutableList()))
|
|
.publishedComments(
|
|
proto.getPublishedCommentList().stream()
|
|
.map(r -> GSON.fromJson(r, Comment.class))
|
|
.collect(toImmutableListMultimap(Comment::getCommitId, c -> c)))
|
|
.updateCount(proto.getUpdateCount());
|
|
return b.build();
|
|
}
|
|
|
|
private static <P extends MessageLite, T> T parseProtoFrom(
|
|
ProtoConverter<P, T> converter, ByteString byteString) {
|
|
P message = Protos.parseUnchecked(converter.getParser(), byteString);
|
|
return converter.fromProto(message);
|
|
}
|
|
|
|
private static ChangeColumns toChangeColumns(Change.Id changeId, ChangeColumnsProto proto) {
|
|
ChangeColumns.Builder b =
|
|
ChangeColumns.builder()
|
|
.changeKey(Change.key(proto.getChangeKey()))
|
|
.createdOn(new Timestamp(proto.getCreatedOn()))
|
|
.lastUpdatedOn(new Timestamp(proto.getLastUpdatedOn()))
|
|
.owner(Account.id(proto.getOwner()))
|
|
.branch(proto.getBranch());
|
|
if (proto.getHasCurrentPatchSetId()) {
|
|
b.currentPatchSetId(PatchSet.id(changeId, proto.getCurrentPatchSetId()));
|
|
}
|
|
b.subject(proto.getSubject());
|
|
if (proto.getHasTopic()) {
|
|
b.topic(proto.getTopic());
|
|
}
|
|
if (proto.getHasOriginalSubject()) {
|
|
b.originalSubject(proto.getOriginalSubject());
|
|
}
|
|
if (proto.getHasSubmissionId()) {
|
|
b.submissionId(proto.getSubmissionId());
|
|
}
|
|
if (proto.getHasAssignee()) {
|
|
b.assignee(Account.id(proto.getAssignee()));
|
|
}
|
|
if (proto.getHasStatus()) {
|
|
b.status(STATUS_CONVERTER.convert(proto.getStatus()));
|
|
}
|
|
b.isPrivate(proto.getIsPrivate())
|
|
.workInProgress(proto.getWorkInProgress())
|
|
.reviewStarted(proto.getReviewStarted());
|
|
if (proto.getHasRevertOf()) {
|
|
b.revertOf(Change.id(proto.getRevertOf()));
|
|
}
|
|
return b.build();
|
|
}
|
|
|
|
private static ReviewerSet toReviewerSet(List<ReviewerSetEntryProto> protos) {
|
|
ImmutableTable.Builder<ReviewerStateInternal, Account.Id, Timestamp> b =
|
|
ImmutableTable.builder();
|
|
for (ReviewerSetEntryProto e : protos) {
|
|
b.put(
|
|
REVIEWER_STATE_CONVERTER.convert(e.getState()),
|
|
Account.id(e.getAccountId()),
|
|
new Timestamp(e.getTimestamp()));
|
|
}
|
|
return ReviewerSet.fromTable(b.build());
|
|
}
|
|
|
|
private static ReviewerByEmailSet toReviewerByEmailSet(
|
|
List<ReviewerByEmailSetEntryProto> protos) {
|
|
ImmutableTable.Builder<ReviewerStateInternal, Address, Timestamp> b =
|
|
ImmutableTable.builder();
|
|
for (ReviewerByEmailSetEntryProto e : protos) {
|
|
b.put(
|
|
REVIEWER_STATE_CONVERTER.convert(e.getState()),
|
|
Address.parse(e.getAddress()),
|
|
new Timestamp(e.getTimestamp()));
|
|
}
|
|
return ReviewerByEmailSet.fromTable(b.build());
|
|
}
|
|
|
|
private static ImmutableList<ReviewerStatusUpdate> toReviewerStatusUpdateList(
|
|
List<ReviewerStatusUpdateProto> protos) {
|
|
ImmutableList.Builder<ReviewerStatusUpdate> b = ImmutableList.builder();
|
|
for (ReviewerStatusUpdateProto proto : protos) {
|
|
b.add(
|
|
ReviewerStatusUpdate.create(
|
|
new Timestamp(proto.getDate()),
|
|
Account.id(proto.getUpdatedBy()),
|
|
Account.id(proto.getReviewer()),
|
|
REVIEWER_STATE_CONVERTER.convert(proto.getState())));
|
|
}
|
|
return b.build();
|
|
}
|
|
}
|
|
}
|