| // 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.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 java.util.Set; | |
| 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) { | |
| Collection<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) { | |
| Set<String> hashtags = notes.load().getHashtags(); | |
| if (!hashtags.isEmpty()) { | |
| changeAttribute.hashtags = new ArrayList<>(hashtags.size()); | |
| changeAttribute.hashtags.addAll(hashtags); | |
| } | |
| } | |
| } |