Line data Source code
1 : // Copyright (C) 2013 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.change; 16 : 17 : import static com.google.common.collect.ImmutableList.toImmutableList; 18 : import static com.google.common.collect.ImmutableSortedSet.toImmutableSortedSet; 19 : import static java.util.Comparator.naturalOrder; 20 : 21 : import com.google.common.collect.ImmutableList; 22 : import com.google.common.collect.ImmutableSortedSet; 23 : import com.google.common.collect.ListMultimap; 24 : import com.google.common.collect.Lists; 25 : import com.google.common.collect.MultimapBuilder; 26 : import com.google.gerrit.entities.Project; 27 : import com.google.gerrit.extensions.api.changes.IncludedInInfo; 28 : import com.google.gerrit.extensions.config.ExternalIncludedIn; 29 : import com.google.gerrit.extensions.restapi.BadRequestException; 30 : import com.google.gerrit.extensions.restapi.ResourceConflictException; 31 : import com.google.gerrit.extensions.restapi.RestApiException; 32 : import com.google.gerrit.server.git.GitRepositoryManager; 33 : import com.google.gerrit.server.permissions.PermissionBackend; 34 : import com.google.gerrit.server.permissions.PermissionBackend.RefFilterOptions; 35 : import com.google.gerrit.server.permissions.PermissionBackendException; 36 : import com.google.gerrit.server.plugincontext.PluginSetContext; 37 : import com.google.inject.Inject; 38 : import com.google.inject.Singleton; 39 : import java.io.IOException; 40 : import java.util.Collection; 41 : import java.util.List; 42 : import java.util.Set; 43 : import java.util.stream.Collectors; 44 : import org.eclipse.jgit.errors.IncorrectObjectTypeException; 45 : import org.eclipse.jgit.errors.MissingObjectException; 46 : import org.eclipse.jgit.lib.Constants; 47 : import org.eclipse.jgit.lib.ObjectId; 48 : import org.eclipse.jgit.lib.Ref; 49 : import org.eclipse.jgit.lib.RefDatabase; 50 : import org.eclipse.jgit.lib.Repository; 51 : import org.eclipse.jgit.revwalk.RevCommit; 52 : import org.eclipse.jgit.revwalk.RevWalk; 53 : 54 : @Singleton 55 : public class IncludedIn { 56 : private final GitRepositoryManager repoManager; 57 : private final PermissionBackend permissionBackend; 58 : private final PluginSetContext<ExternalIncludedIn> externalIncludedIn; 59 : 60 : @Inject 61 : IncludedIn( 62 : GitRepositoryManager repoManager, 63 : PermissionBackend permissionBackend, 64 145 : PluginSetContext<ExternalIncludedIn> externalIncludedIn) { 65 145 : this.repoManager = repoManager; 66 145 : this.permissionBackend = permissionBackend; 67 145 : this.externalIncludedIn = externalIncludedIn; 68 145 : } 69 : 70 : public IncludedInInfo apply(Project.NameKey project, String revisionId) 71 : throws RestApiException, IOException, PermissionBackendException { 72 3 : try (Repository r = repoManager.openRepository(project); 73 3 : RevWalk rw = new RevWalk(r)) { 74 3 : rw.setRetainBody(false); 75 : RevCommit rev; 76 : try { 77 3 : rev = rw.parseCommit(ObjectId.fromString(revisionId)); 78 0 : } catch (IncorrectObjectTypeException err) { 79 0 : throw new BadRequestException(err.getMessage()); 80 0 : } catch (MissingObjectException err) { 81 0 : throw new ResourceConflictException(err.getMessage()); 82 3 : } 83 : 84 3 : RefDatabase refDb = r.getRefDatabase(); 85 3 : Collection<Ref> tags = refDb.getRefsByPrefix(Constants.R_TAGS); 86 3 : Collection<Ref> branches = refDb.getRefsByPrefix(Constants.R_HEADS); 87 3 : List<Ref> allTagsAndBranches = Lists.newArrayListWithCapacity(tags.size() + branches.size()); 88 3 : allTagsAndBranches.addAll(tags); 89 3 : allTagsAndBranches.addAll(branches); 90 : 91 3 : Set<String> allMatchingTagsAndBranches = 92 3 : rw.getMergedInto(rev, IncludedInUtil.getSortedRefs(allTagsAndBranches, rw)).stream() 93 3 : .map(Ref::getName) 94 3 : .collect(Collectors.toSet()); 95 : 96 : // Filter branches and tags according to their visbility by the user 97 3 : ImmutableSortedSet<String> filteredBranches = 98 3 : sortedShortNames( 99 3 : filterReadableRefs( 100 3 : project, getMatchingRefNames(allMatchingTagsAndBranches, branches))); 101 3 : ImmutableSortedSet<String> filteredTags = 102 3 : sortedShortNames( 103 3 : filterReadableRefs(project, getMatchingRefNames(allMatchingTagsAndBranches, tags))); 104 : 105 3 : ListMultimap<String, String> external = MultimapBuilder.hashKeys().arrayListValues().build(); 106 3 : externalIncludedIn.runEach( 107 : ext -> { 108 0 : ListMultimap<String, String> extIncludedIns = 109 0 : ext.getIncludedIn(project.get(), rev.name(), filteredBranches, filteredTags); 110 0 : if (extIncludedIns != null) { 111 0 : external.putAll(extIncludedIns); 112 : } 113 0 : }); 114 : 115 3 : return new IncludedInInfo( 116 3 : filteredBranches, filteredTags, (!external.isEmpty() ? external.asMap() : null)); 117 : } 118 : } 119 : 120 : /** 121 : * Filter readable branches or tags according to the caller's refs visibility. 122 : * 123 : * @param project specific Gerrit project. 124 : * @param inputRefs a list of branches (in short name) as strings 125 : */ 126 : private Collection<String> filterReadableRefs( 127 : Project.NameKey project, ImmutableList<Ref> inputRefs) 128 : throws IOException, PermissionBackendException { 129 3 : PermissionBackend.ForProject perm = permissionBackend.currentUser().project(project); 130 3 : try (Repository repo = repoManager.openRepository(project)) { 131 3 : return perm.filter(inputRefs, repo, RefFilterOptions.defaults()).stream() 132 3 : .map(Ref::getName) 133 3 : .collect(toImmutableList()); 134 : } 135 : } 136 : 137 : /** 138 : * Returns the short names of refs which are as well in the matchingRefs list as well as in the 139 : * allRef list. 140 : */ 141 : private static ImmutableList<Ref> getMatchingRefNames( 142 : Set<String> matchingRefs, Collection<Ref> allRefs) { 143 3 : return allRefs.stream() 144 3 : .filter(r -> matchingRefs.contains(r.getName())) 145 3 : .distinct() 146 3 : .collect(toImmutableList()); 147 : } 148 : 149 : private ImmutableSortedSet<String> sortedShortNames(Collection<String> refs) { 150 3 : return refs.stream() 151 3 : .map(Repository::shortenRefName) 152 3 : .collect(toImmutableSortedSet(naturalOrder())); 153 : } 154 : }