Line data Source code
1 : // Copyright (C) 2016 The Android Open Source Project 2 : // 3 : // Licensed under the Apache License, Version 2.0 (the "License"); 4 : // you may not use this file except in compliance with the License. 5 : // You may obtain a copy of the License at 6 : // 7 : // http://www.apache.org/licenses/LICENSE-2.0 8 : // 9 : // Unless required by applicable law or agreed to in writing, software 10 : // distributed under the License is distributed on an "AS IS" BASIS, 11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 : // See the License for the specific language governing permissions and 13 : // limitations under the License. 14 : 15 : package com.google.gerrit.server; 16 : 17 : import static com.google.common.base.Preconditions.checkArgument; 18 : import static com.google.gerrit.server.project.ProjectCache.illegalState; 19 : import static java.util.Objects.requireNonNull; 20 : 21 : import com.google.common.collect.ImmutableCollection; 22 : import com.google.common.collect.ImmutableMap; 23 : import com.google.common.collect.Maps; 24 : import com.google.gerrit.common.Nullable; 25 : import com.google.gerrit.entities.Change; 26 : import com.google.gerrit.entities.LabelFunction; 27 : import com.google.gerrit.entities.LabelType; 28 : import com.google.gerrit.entities.PatchSet; 29 : import com.google.gerrit.entities.PatchSetApproval; 30 : import com.google.gerrit.entities.Project; 31 : import com.google.gerrit.extensions.restapi.ResourceConflictException; 32 : import com.google.gerrit.server.approval.ApprovalsUtil; 33 : import com.google.gerrit.server.git.GitRepositoryManager; 34 : import com.google.gerrit.server.notedb.ChangeNotes; 35 : import com.google.gerrit.server.notedb.ChangeUpdate; 36 : import com.google.gerrit.server.project.ProjectCache; 37 : import com.google.gerrit.server.project.ProjectState; 38 : import com.google.inject.Inject; 39 : import com.google.inject.Provider; 40 : import com.google.inject.Singleton; 41 : import java.io.IOException; 42 : import java.util.List; 43 : import java.util.Optional; 44 : import java.util.Set; 45 : import org.eclipse.jgit.lib.ObjectId; 46 : import org.eclipse.jgit.lib.Repository; 47 : import org.eclipse.jgit.revwalk.RevCommit; 48 : import org.eclipse.jgit.revwalk.RevWalk; 49 : 50 : /** Utilities for manipulating patch sets. */ 51 : @Singleton 52 : public class PatchSetUtil { 53 : private final Provider<ApprovalsUtil> approvalsUtilProvider; 54 : private final ProjectCache projectCache; 55 : private final GitRepositoryManager repoManager; 56 : 57 : @Inject 58 : PatchSetUtil( 59 : Provider<ApprovalsUtil> approvalsUtilProvider, 60 : ProjectCache projectCache, 61 150 : GitRepositoryManager repoManager) { 62 150 : this.approvalsUtilProvider = approvalsUtilProvider; 63 150 : this.projectCache = projectCache; 64 150 : this.repoManager = repoManager; 65 150 : } 66 : 67 : public PatchSet current(ChangeNotes notes) { 68 90 : return get(notes, notes.getChange().currentPatchSetId()); 69 : } 70 : 71 : public PatchSet get(ChangeNotes notes, PatchSet.Id psId) { 72 102 : return notes.load().getPatchSets().get(psId); 73 : } 74 : 75 : public ImmutableCollection<PatchSet> byChange(ChangeNotes notes) { 76 103 : return notes.load().getPatchSets().values(); 77 : } 78 : 79 : public ImmutableMap<PatchSet.Id, PatchSet> byChangeAsMap(ChangeNotes notes) { 80 1 : return notes.load().getPatchSets(); 81 : } 82 : 83 : public ImmutableMap<PatchSet.Id, PatchSet> getAsMap( 84 : ChangeNotes notes, Set<PatchSet.Id> patchSetIds) { 85 9 : return ImmutableMap.copyOf(Maps.filterKeys(notes.load().getPatchSets(), patchSetIds::contains)); 86 : } 87 : 88 : public PatchSet insert( 89 : RevWalk rw, 90 : ChangeUpdate update, 91 : PatchSet.Id psId, 92 : ObjectId commit, 93 : List<String> groups, 94 : @Nullable String pushCertificate, 95 : @Nullable String description) 96 : throws IOException { 97 103 : requireNonNull(groups, "groups may not be null"); 98 103 : ensurePatchSetMatches(psId, update); 99 : 100 103 : update.setCommit(rw, commit, pushCertificate); 101 103 : update.setPsDescription(description); 102 103 : update.setGroups(groups); 103 : 104 103 : return PatchSet.builder() 105 103 : .id(psId) 106 103 : .commitId(commit) 107 103 : .uploader(update.getAccountId()) 108 103 : .createdOn(update.getWhen()) 109 103 : .groups(groups) 110 103 : .pushCertificate(Optional.ofNullable(pushCertificate)) 111 103 : .description(Optional.ofNullable(description)) 112 103 : .build(); 113 : } 114 : 115 : private static void ensurePatchSetMatches(PatchSet.Id psId, ChangeUpdate update) { 116 103 : Change.Id changeId = update.getId(); 117 103 : checkArgument( 118 103 : psId.changeId().equals(changeId), 119 : "cannot modify patch set %s on update for change %s", 120 : psId, 121 : changeId); 122 103 : if (update.getPatchSetId() != null) { 123 103 : checkArgument( 124 103 : update.getPatchSetId().equals(psId), 125 : "cannot modify patch set %s on update for %s", 126 : psId, 127 103 : update.getPatchSetId()); 128 : } else { 129 0 : update.setPatchSetId(psId); 130 : } 131 103 : } 132 : 133 : /** Check if the current patch set of the change is locked. */ 134 : public void checkPatchSetNotLocked(ChangeNotes notes) throws ResourceConflictException { 135 46 : if (isPatchSetLocked(notes)) { 136 1 : throw new ResourceConflictException( 137 1 : String.format("The current patch set of change %s is locked", notes.getChangeId())); 138 : } 139 46 : } 140 : 141 : /** Is the current patch set locked against state changes? */ 142 : public boolean isPatchSetLocked(ChangeNotes notes) { 143 73 : Change change = notes.getChange(); 144 73 : if (change.isMerged()) { 145 0 : return false; 146 : } 147 : 148 73 : ProjectState projectState = 149 73 : projectCache.get(notes.getProjectName()).orElseThrow(illegalState(notes.getProjectName())); 150 : 151 73 : ApprovalsUtil approvalsUtil = approvalsUtilProvider.get(); 152 73 : for (PatchSetApproval ap : approvalsUtil.byPatchSet(notes, change.currentPatchSetId())) { 153 30 : Optional<LabelType> type = projectState.getLabelTypes(notes).byLabel(ap.label()); 154 30 : if (type.isPresent() 155 30 : && ap.value() == 1 156 19 : && type.get().getFunction() == LabelFunction.PATCH_SET_LOCK) { 157 4 : return true; 158 : } 159 30 : } 160 73 : return false; 161 : } 162 : 163 : /** Returns the commit for the given project at the given patchset revision */ 164 : public RevCommit getRevCommit(Project.NameKey project, PatchSet patchSet) throws IOException { 165 4 : try (Repository repo = repoManager.openRepository(project); 166 4 : RevWalk rw = new RevWalk(repo)) { 167 4 : RevCommit src = rw.parseCommit(patchSet.commitId()); 168 4 : rw.parseBody(src); 169 4 : return src; 170 : } 171 : } 172 : }