| // Copyright (C) 2010 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.events; |
| |
| import static java.util.Comparator.comparing; |
| import static java.util.Objects.requireNonNull; |
| |
| import com.google.common.collect.ImmutableSet; |
| import com.google.common.collect.ListMultimap; |
| import com.google.common.collect.Lists; |
| import com.google.common.flogger.FluentLogger; |
| import com.google.gerrit.common.Nullable; |
| import com.google.gerrit.entities.Account; |
| import com.google.gerrit.entities.BranchNameKey; |
| import com.google.gerrit.entities.Change; |
| import com.google.gerrit.entities.ChangeMessage; |
| import com.google.gerrit.entities.HumanComment; |
| import com.google.gerrit.entities.LabelType; |
| import com.google.gerrit.entities.LabelTypes; |
| import com.google.gerrit.entities.LegacySubmitRequirement; |
| import com.google.gerrit.entities.PatchSet; |
| import com.google.gerrit.entities.PatchSetApproval; |
| import com.google.gerrit.entities.SubmitRecord; |
| import com.google.gerrit.entities.UserIdentity; |
| import com.google.gerrit.exceptions.StorageException; |
| import com.google.gerrit.extensions.registration.DynamicItem; |
| import com.google.gerrit.index.IndexConfig; |
| import com.google.gerrit.server.GerritPersonIdent; |
| import com.google.gerrit.server.account.AccountAttributeLoader; |
| 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.approval.ApprovalsUtil; |
| import com.google.gerrit.server.change.ChangeKindCache; |
| import com.google.gerrit.server.config.UrlFormatter; |
| import com.google.gerrit.server.data.AccountAttribute; |
| import com.google.gerrit.server.data.ApprovalAttribute; |
| import com.google.gerrit.server.data.ChangeAttribute; |
| import com.google.gerrit.server.data.DependencyAttribute; |
| import com.google.gerrit.server.data.MessageAttribute; |
| import com.google.gerrit.server.data.PatchAttribute; |
| import com.google.gerrit.server.data.PatchSetAttribute; |
| import com.google.gerrit.server.data.PatchSetCommentAttribute; |
| import com.google.gerrit.server.data.RefUpdateAttribute; |
| import com.google.gerrit.server.data.SubmitLabelAttribute; |
| import com.google.gerrit.server.data.SubmitRecordAttribute; |
| import com.google.gerrit.server.data.SubmitRequirementAttribute; |
| import com.google.gerrit.server.data.TrackingIdAttribute; |
| import com.google.gerrit.server.notedb.ChangeNotes; |
| import com.google.gerrit.server.patch.DiffNotAvailableException; |
| import com.google.gerrit.server.patch.DiffOperations; |
| import com.google.gerrit.server.patch.DiffOptions; |
| import com.google.gerrit.server.patch.FilePathAdapter; |
| import com.google.gerrit.server.patch.filediff.FileDiffOutput; |
| import com.google.gerrit.server.query.change.ChangeData; |
| import com.google.gerrit.server.query.change.InternalChangeQuery; |
| import com.google.gerrit.server.util.AccountTemplateUtil; |
| 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.List; |
| import java.util.Map; |
| import java.util.Optional; |
| import org.eclipse.jgit.lib.ObjectId; |
| import org.eclipse.jgit.lib.PersonIdent; |
| import org.eclipse.jgit.revwalk.RevCommit; |
| import org.eclipse.jgit.revwalk.RevWalk; |
| |
| @Singleton |
| public class EventFactory { |
| private static final FluentLogger logger = FluentLogger.forEnclosingClass(); |
| |
| private final AccountCache accountCache; |
| private final DynamicItem<UrlFormatter> urlFormatter; |
| private final DiffOperations diffOperations; |
| private final Emails emails; |
| private final Provider<PersonIdent> myIdent; |
| private final ChangeData.Factory changeDataFactory; |
| private final ApprovalsUtil approvalsUtil; |
| private final ChangeKindCache changeKindCache; |
| private final Provider<InternalChangeQuery> queryProvider; |
| private final IndexConfig indexConfig; |
| private final AccountTemplateUtil accountTemplateUtil; |
| |
| @Inject |
| EventFactory( |
| AccountCache accountCache, |
| Emails emails, |
| DynamicItem<UrlFormatter> urlFormatter, |
| DiffOperations diffOperations, |
| @GerritPersonIdent Provider<PersonIdent> myIdent, |
| ChangeData.Factory changeDataFactory, |
| ApprovalsUtil approvalsUtil, |
| ChangeKindCache changeKindCache, |
| Provider<InternalChangeQuery> queryProvider, |
| IndexConfig indexConfig, |
| AccountTemplateUtil accountTemplateUtil) { |
| this.accountCache = accountCache; |
| this.urlFormatter = urlFormatter; |
| this.emails = emails; |
| this.diffOperations = diffOperations; |
| this.myIdent = myIdent; |
| this.changeDataFactory = changeDataFactory; |
| this.approvalsUtil = approvalsUtil; |
| this.changeKindCache = changeKindCache; |
| this.queryProvider = queryProvider; |
| this.indexConfig = indexConfig; |
| this.accountTemplateUtil = accountTemplateUtil; |
| } |
| |
| public ChangeAttribute asChangeAttribute(Change change, AccountAttributeLoader accountLoader) { |
| ChangeAttribute a = new ChangeAttribute(); |
| a.project = change.getProject().get(); |
| a.branch = change.getDest().shortName(); |
| a.topic = change.getTopic(); |
| a.id = change.getKey().get(); |
| a.number = change.getId().get(); |
| a.subject = change.getSubject(); |
| a.url = getChangeUrl(change); |
| a.owner = asAccountAttribute(change.getOwner(), accountLoader); |
| a.status = change.getStatus(); |
| a.createdOn = change.getCreatedOn().getEpochSecond(); |
| a.wip = change.isWorkInProgress() ? true : null; |
| a.isPrivate = change.isPrivate() ? true : null; |
| a.cherryPickOfChange = |
| change.getCherryPickOf() != null ? change.getCherryPickOf().changeId().get() : null; |
| a.cherryPickOfPatchSet = |
| change.getCherryPickOf() != null ? change.getCherryPickOf().get() : null; |
| return a; |
| } |
| |
| /** Create a {@link ChangeAttribute} instance from the specified change. */ |
| public ChangeAttribute asChangeAttribute(Change change, ChangeNotes notes) { |
| ChangeAttribute a = asChangeAttribute(change, (AccountAttributeLoader) null); |
| addHashTags(a, notes); |
| addCommitMessage(a, notes); |
| return a; |
| } |
| /** |
| * Create a {@link RefUpdateAttribute} for the given old ObjectId, new ObjectId, and branch that |
| * is suitable for serialization to JSON. |
| */ |
| public RefUpdateAttribute asRefUpdateAttribute( |
| ObjectId oldId, ObjectId newId, BranchNameKey refName) { |
| RefUpdateAttribute ru = new RefUpdateAttribute(); |
| ru.newRev = newId != null ? newId.getName() : ObjectId.zeroId().getName(); |
| ru.oldRev = oldId != null ? oldId.getName() : ObjectId.zeroId().getName(); |
| ru.project = refName.project().get(); |
| ru.refName = refName.branch(); |
| return ru; |
| } |
| |
| /** Extend the existing {@link ChangeAttribute} with additional fields. */ |
| public void extend(ChangeAttribute a, Change change) { |
| a.lastUpdated = change.getLastUpdatedOn().getEpochSecond(); |
| a.open = change.isNew(); |
| } |
| |
| /** Add allReviewers to an existing {@link ChangeAttribute}. */ |
| public void addAllReviewers( |
| ChangeAttribute a, ChangeNotes notes, AccountAttributeLoader accountLoader) { |
| ImmutableSet<Account.Id> reviewers = approvalsUtil.getReviewers(notes).all(); |
| if (!reviewers.isEmpty()) { |
| a.allReviewers = Lists.newArrayListWithCapacity(reviewers.size()); |
| for (Account.Id id : reviewers) { |
| a.allReviewers.add(asAccountAttribute(id, accountLoader)); |
| } |
| } |
| } |
| |
| /** Add submitRecords to an existing {@link ChangeAttribute}. */ |
| public void addSubmitRecords( |
| ChangeAttribute ca, List<SubmitRecord> submitRecords, AccountAttributeLoader accountLoader) { |
| ca.submitRecords = new ArrayList<>(); |
| |
| for (SubmitRecord submitRecord : submitRecords) { |
| SubmitRecordAttribute sa = new SubmitRecordAttribute(); |
| sa.status = submitRecord.status.name(); |
| if (submitRecord.status != SubmitRecord.Status.RULE_ERROR) { |
| addSubmitRecordLabels(submitRecord, sa, accountLoader); |
| addSubmitRecordRequirements(submitRecord, sa); |
| } |
| ca.submitRecords.add(sa); |
| } |
| // Remove empty lists so a confusing label won't be displayed in the output. |
| if (ca.submitRecords.isEmpty()) { |
| ca.submitRecords = null; |
| } |
| } |
| |
| private void addSubmitRecordLabels( |
| SubmitRecord submitRecord, SubmitRecordAttribute sa, AccountAttributeLoader accountLoader) { |
| if (submitRecord.labels != null && !submitRecord.labels.isEmpty()) { |
| sa.labels = new ArrayList<>(); |
| for (SubmitRecord.Label lbl : submitRecord.labels) { |
| SubmitLabelAttribute la = new SubmitLabelAttribute(); |
| la.label = lbl.label; |
| la.status = lbl.status.name(); |
| if (lbl.appliedBy != null) { |
| la.by = asAccountAttribute(lbl.appliedBy, accountLoader); |
| } |
| sa.labels.add(la); |
| } |
| } |
| } |
| |
| private void addSubmitRecordRequirements(SubmitRecord submitRecord, SubmitRecordAttribute sa) { |
| if (submitRecord.requirements != null && !submitRecord.requirements.isEmpty()) { |
| sa.requirements = new ArrayList<>(); |
| for (LegacySubmitRequirement req : submitRecord.requirements) { |
| SubmitRequirementAttribute re = new SubmitRequirementAttribute(); |
| re.fallbackText = req.fallbackText(); |
| re.type = req.type(); |
| sa.requirements.add(re); |
| } |
| } |
| } |
| |
| public void addDependencies(RevWalk rw, ChangeAttribute ca, Change change, PatchSet currentPs) { |
| if (change == null || currentPs == null) { |
| return; |
| } |
| ca.dependsOn = new ArrayList<>(); |
| ca.neededBy = new ArrayList<>(); |
| try { |
| addDependsOn(rw, ca, change, currentPs); |
| addNeededBy(rw, ca, change, currentPs); |
| } catch (StorageException | IOException e) { |
| // Squash DB exceptions and leave dependency lists partially filled. |
| } |
| // Remove empty lists so a confusing label won't be displayed in the output. |
| if (ca.dependsOn.isEmpty()) { |
| ca.dependsOn = null; |
| } |
| if (ca.neededBy.isEmpty()) { |
| ca.neededBy = null; |
| } |
| } |
| |
| private void addDependsOn(RevWalk rw, ChangeAttribute ca, Change change, PatchSet currentPs) |
| throws IOException { |
| RevCommit commit = rw.parseCommit(currentPs.commitId()); |
| final List<String> parentNames = new ArrayList<>(commit.getParentCount()); |
| for (RevCommit p : commit.getParents()) { |
| parentNames.add(p.name()); |
| } |
| |
| // Find changes in this project having a patch set matching any parent of |
| // this patch set's revision. |
| for (ChangeData cd : queryProvider.get().byProjectCommits(change.getProject(), parentNames)) { |
| for (PatchSet ps : cd.patchSets()) { |
| for (String p : parentNames) { |
| if (!ps.commitId().name().equals(p)) { |
| continue; |
| } |
| ca.dependsOn.add(newDependsOn(requireNonNull(cd.change()), ps)); |
| } |
| } |
| } |
| // Sort by original parent order. |
| ca.dependsOn.sort( |
| comparing( |
| d -> { |
| for (int i = 0; i < parentNames.size(); i++) { |
| if (parentNames.get(i).equals(d.revision)) { |
| return i; |
| } |
| } |
| return parentNames.size() + 1; |
| })); |
| } |
| |
| private void addNeededBy(RevWalk rw, ChangeAttribute ca, Change change, PatchSet currentPs) |
| throws IOException { |
| if (currentPs.groups().isEmpty()) { |
| return; |
| } |
| String rev = currentPs.commitId().name(); |
| // Find changes in the same related group as this patch set, having a patch |
| // set whose parent matches this patch set's revision. |
| for (ChangeData cd : |
| InternalChangeQuery.byProjectGroups( |
| queryProvider, indexConfig, change.getProject(), currentPs.groups())) { |
| PATCH_SETS: |
| for (PatchSet ps : cd.patchSets()) { |
| RevCommit commit = rw.parseCommit(ps.commitId()); |
| for (RevCommit p : commit.getParents()) { |
| if (!p.name().equals(rev)) { |
| continue; |
| } |
| ca.neededBy.add(newNeededBy(requireNonNull(cd.change()), ps)); |
| continue PATCH_SETS; |
| } |
| } |
| } |
| } |
| |
| private DependencyAttribute newDependsOn(Change c, PatchSet ps) { |
| DependencyAttribute d = newDependencyAttribute(c, ps); |
| d.isCurrentPatchSet = ps.id().equals(c.currentPatchSetId()); |
| return d; |
| } |
| |
| private DependencyAttribute newNeededBy(Change c, PatchSet ps) { |
| return newDependencyAttribute(c, ps); |
| } |
| |
| private DependencyAttribute newDependencyAttribute(Change c, PatchSet ps) { |
| DependencyAttribute d = new DependencyAttribute(); |
| d.number = c.getId().get(); |
| d.id = c.getKey().toString(); |
| d.revision = ps.commitId().name(); |
| d.ref = ps.refName(); |
| return d; |
| } |
| |
| public void addTrackingIds(ChangeAttribute a, ListMultimap<String, String> set) { |
| if (!set.isEmpty()) { |
| a.trackingIds = new ArrayList<>(set.size()); |
| for (Map.Entry<String, Collection<String>> e : set.asMap().entrySet()) { |
| for (String id : e.getValue()) { |
| TrackingIdAttribute t = new TrackingIdAttribute(); |
| t.system = e.getKey(); |
| t.id = id; |
| a.trackingIds.add(t); |
| } |
| } |
| } |
| } |
| |
| public void addCommitMessage(ChangeAttribute a, String commitMessage) { |
| a.commitMessage = commitMessage; |
| } |
| |
| private void addCommitMessage(ChangeAttribute changeAttribute, ChangeNotes notes) { |
| try { |
| addCommitMessage(changeAttribute, changeDataFactory.create(notes).commitMessage()); |
| } catch (Exception e) { |
| logger.atSevere().withCause(e).log( |
| "Error while getting full commit message for change %d", changeAttribute.number); |
| } |
| } |
| |
| public void addPatchSets( |
| RevWalk revWalk, |
| ChangeAttribute ca, |
| Collection<PatchSet> ps, |
| Map<PatchSet.Id, Collection<PatchSetApproval>> approvals, |
| LabelTypes labelTypes, |
| AccountAttributeLoader accountLoader) { |
| addPatchSets(revWalk, ca, ps, approvals, false, null, labelTypes, accountLoader); |
| } |
| |
| public void addPatchSets( |
| RevWalk revWalk, |
| ChangeAttribute ca, |
| Collection<PatchSet> ps, |
| Map<PatchSet.Id, Collection<PatchSetApproval>> approvals, |
| boolean includeFiles, |
| Change change, |
| LabelTypes labelTypes, |
| AccountAttributeLoader accountLoader) { |
| if (!ps.isEmpty()) { |
| ca.patchSets = new ArrayList<>(ps.size()); |
| for (PatchSet p : ps) { |
| PatchSetAttribute psa = asPatchSetAttribute(revWalk, change, p, accountLoader); |
| if (approvals != null) { |
| addApprovals(psa, p.id(), approvals, labelTypes, accountLoader); |
| } |
| ca.patchSets.add(psa); |
| if (includeFiles) { |
| addPatchSetFileNames(psa, change, p); |
| } |
| } |
| } |
| } |
| |
| public void addPatchSetComments( |
| PatchSetAttribute patchSetAttribute, |
| Collection<HumanComment> comments, |
| AccountAttributeLoader accountLoader) { |
| for (HumanComment comment : comments) { |
| if (comment.key.patchSetId == patchSetAttribute.number) { |
| if (patchSetAttribute.comments == null) { |
| patchSetAttribute.comments = new ArrayList<>(); |
| } |
| patchSetAttribute.comments.add(asPatchSetLineAttribute(comment, accountLoader)); |
| } |
| } |
| } |
| |
| public void addPatchSetFileNames( |
| PatchSetAttribute patchSetAttribute, Change change, PatchSet patchSet) { |
| try { |
| Map<String, FileDiffOutput> modifiedFiles = |
| diffOperations.listModifiedFilesAgainstParent( |
| change.getProject(), patchSet.commitId(), /* parentNum= */ 0, DiffOptions.DEFAULTS); |
| |
| for (FileDiffOutput diff : modifiedFiles.values()) { |
| if (patchSetAttribute.files == null) { |
| patchSetAttribute.files = new ArrayList<>(); |
| } |
| |
| PatchAttribute p = new PatchAttribute(); |
| p.file = FilePathAdapter.getNewPath(diff.oldPath(), diff.newPath(), diff.changeType()); |
| p.fileOld = FilePathAdapter.getOldPath(diff.oldPath(), diff.changeType()); |
| p.type = diff.changeType(); |
| p.deletions -= diff.deletions(); |
| p.insertions = diff.insertions(); |
| patchSetAttribute.files.add(p); |
| } |
| } catch (DiffNotAvailableException e) { |
| logger.atSevere().withCause(e).log("Cannot get patch list"); |
| } |
| } |
| |
| public void addComments( |
| ChangeAttribute ca, |
| Collection<ChangeMessage> messages, |
| AccountAttributeLoader accountLoader) { |
| if (!messages.isEmpty()) { |
| ca.comments = new ArrayList<>(); |
| for (ChangeMessage message : messages) { |
| ca.comments.add(asMessageAttribute(message, accountLoader)); |
| } |
| } |
| } |
| |
| public PatchSetAttribute asPatchSetAttribute(RevWalk revWalk, Change change, PatchSet patchSet) { |
| return asPatchSetAttribute(revWalk, change, patchSet, null); |
| } |
| |
| /** Create a PatchSetAttribute for the given patchset suitable for serialization to JSON. */ |
| public PatchSetAttribute asPatchSetAttribute( |
| RevWalk revWalk, Change change, PatchSet patchSet, AccountAttributeLoader accountLoader) { |
| PatchSetAttribute p = new PatchSetAttribute(); |
| p.revision = patchSet.commitId().name(); |
| p.number = patchSet.number(); |
| p.ref = patchSet.refName(); |
| p.uploader = asAccountAttribute(patchSet.uploader(), accountLoader); |
| p.createdOn = patchSet.createdOn().getEpochSecond(); |
| PatchSet.Id pId = patchSet.id(); |
| try { |
| p.parents = new ArrayList<>(); |
| RevCommit c = revWalk.parseCommit(ObjectId.fromString(p.revision)); |
| for (RevCommit parent : c.getParents()) { |
| p.parents.add(parent.name()); |
| } |
| |
| UserIdentity author = emails.toUserIdentity(c.getAuthorIdent()); |
| if (author.getAccount() == null) { |
| p.author = new AccountAttribute(); |
| p.author.email = author.getEmail(); |
| p.author.name = author.getName(); |
| p.author.username = ""; |
| } else { |
| p.author = asAccountAttribute(author.getAccount(), accountLoader); |
| } |
| |
| Map<String, FileDiffOutput> modifiedFiles = |
| diffOperations.listModifiedFilesAgainstParent( |
| change.getProject(), patchSet.commitId(), /* parentNum= */ 0, DiffOptions.DEFAULTS); |
| for (FileDiffOutput fileDiff : modifiedFiles.values()) { |
| p.sizeDeletions += fileDiff.deletions(); |
| p.sizeInsertions += fileDiff.insertions(); |
| } |
| p.kind = changeKindCache.getChangeKind(change, patchSet); |
| } catch (IOException | StorageException e) { |
| logger.atSevere().withCause(e).log("Cannot load patch set data for %s", patchSet.id()); |
| } catch (DiffNotAvailableException e) { |
| logger.atSevere().withCause(e).log("Cannot get size information for %s.", pId); |
| } |
| return p; |
| } |
| |
| public void addApprovals( |
| PatchSetAttribute p, |
| PatchSet.Id id, |
| Map<PatchSet.Id, Collection<PatchSetApproval>> all, |
| LabelTypes labelTypes, |
| AccountAttributeLoader accountLoader) { |
| Collection<PatchSetApproval> list = all.get(id); |
| if (list != null) { |
| addApprovals(p, list, labelTypes, accountLoader); |
| } |
| } |
| |
| public void addApprovals( |
| PatchSetAttribute p, |
| Collection<PatchSetApproval> list, |
| LabelTypes labelTypes, |
| AccountAttributeLoader accountLoader) { |
| if (!list.isEmpty()) { |
| p.approvals = new ArrayList<>(list.size()); |
| for (PatchSetApproval a : list) { |
| if (a.value() != 0) { |
| p.approvals.add(asApprovalAttribute(a, labelTypes, accountLoader)); |
| } |
| } |
| if (p.approvals.isEmpty()) { |
| p.approvals = null; |
| } |
| } |
| } |
| |
| public AccountAttribute asAccountAttribute(Account.Id id, AccountAttributeLoader accountLoader) { |
| return accountLoader != null ? accountLoader.get(id) : asAccountAttribute(id); |
| } |
| |
| /** Create an AuthorAttribute for the given account suitable for serialization to JSON. */ |
| @Nullable |
| public AccountAttribute asAccountAttribute(Account.Id id) { |
| if (id == null) { |
| return null; |
| } |
| return accountCache.get(id).map(this::asAccountAttribute).orElse(null); |
| } |
| |
| /** Create an AuthorAttribute for the given account suitable for serialization to JSON. */ |
| public AccountAttribute asAccountAttribute(AccountState accountState) { |
| AccountAttribute who = new AccountAttribute(); |
| who.name = accountState.account().fullName(); |
| who.email = accountState.account().preferredEmail(); |
| who.username = accountState.userName().orElse(null); |
| return who; |
| } |
| |
| /** Create an AuthorAttribute for the given person ident suitable for serialization to JSON. */ |
| public AccountAttribute asAccountAttribute(PersonIdent ident) { |
| AccountAttribute who = new AccountAttribute(); |
| who.name = ident.getName(); |
| who.email = ident.getEmailAddress(); |
| return who; |
| } |
| |
| /** |
| * Create an ApprovalAttribute for the given approval suitable for serialization to JSON. |
| * |
| * @param labelTypes label types for the containing project |
| * @return object suitable for serialization to JSON |
| */ |
| public ApprovalAttribute asApprovalAttribute( |
| PatchSetApproval approval, LabelTypes labelTypes, AccountAttributeLoader accountLoader) { |
| ApprovalAttribute a = new ApprovalAttribute(); |
| a.type = approval.labelId().get(); |
| a.value = Short.toString(approval.value()); |
| a.by = asAccountAttribute(approval.accountId(), accountLoader); |
| a.grantedOn = approval.granted().getEpochSecond(); |
| a.oldValue = null; |
| |
| Optional<LabelType> lt = labelTypes.byLabel(approval.labelId()); |
| lt.ifPresent(l -> a.description = l.getName()); |
| return a; |
| } |
| |
| public MessageAttribute asMessageAttribute( |
| ChangeMessage message, AccountAttributeLoader accountLoader) { |
| MessageAttribute a = new MessageAttribute(); |
| a.timestamp = message.getWrittenOn().getEpochSecond(); |
| a.reviewer = |
| message.getAuthor() != null |
| ? asAccountAttribute(message.getAuthor(), accountLoader) |
| : asAccountAttribute(myIdent.get()); |
| a.message = accountTemplateUtil.replaceTemplates(message.getMessage()); |
| return a; |
| } |
| |
| public PatchSetCommentAttribute asPatchSetLineAttribute( |
| HumanComment c, AccountAttributeLoader accountLoader) { |
| PatchSetCommentAttribute a = new PatchSetCommentAttribute(); |
| a.reviewer = asAccountAttribute(c.author.getId(), accountLoader); |
| a.file = c.key.filename; |
| a.line = c.lineNbr; |
| a.message = c.message; |
| return a; |
| } |
| |
| /** Get a link to the change; null if the server doesn't know its own address. */ |
| @Nullable |
| private String getChangeUrl(Change change) { |
| if (change != null) { |
| return urlFormatter.get().getChangeViewUrl(change.getProject(), change.getId()).orElse(null); |
| } |
| return null; |
| } |
| |
| private void addHashTags(ChangeAttribute changeAttribute, ChangeNotes notes) { |
| ImmutableSet<String> hashtags = notes.load().getHashtags(); |
| if (!hashtags.isEmpty()) { |
| changeAttribute.hashtags = new ArrayList<>(hashtags.size()); |
| changeAttribute.hashtags.addAll(hashtags); |
| } |
| } |
| } |