Line data Source code
1 : // Copyright (C) 2021 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.base.Preconditions.checkArgument; 18 : import static java.util.stream.Collectors.toSet; 19 : 20 : import com.google.common.annotations.VisibleForTesting; 21 : import com.google.common.flogger.FluentLogger; 22 : import com.google.gerrit.entities.PatchSet; 23 : import com.google.gerrit.index.IndexConfig; 24 : import com.google.gerrit.server.permissions.PermissionBackendException; 25 : import com.google.gerrit.server.query.change.ChangeData; 26 : import com.google.gerrit.server.query.change.InternalChangeQuery; 27 : import com.google.inject.Inject; 28 : import com.google.inject.Provider; 29 : import com.google.inject.Singleton; 30 : import java.io.IOException; 31 : import java.util.ArrayList; 32 : import java.util.Collection; 33 : import java.util.Collections; 34 : import java.util.List; 35 : import java.util.Set; 36 : 37 : /** Utility class that gets the ancestor changes and the descendent changes of a specific change. */ 38 : @Singleton 39 : public class GetRelatedChangesUtil { 40 145 : private static final FluentLogger logger = FluentLogger.forEnclosingClass(); 41 : 42 : private final Provider<InternalChangeQuery> queryProvider; 43 : private final RelatedChangesSorter sorter; 44 : private final IndexConfig indexConfig; 45 : private final ChangeData.Factory changeDataFactory; 46 : 47 : @Inject 48 : GetRelatedChangesUtil( 49 : Provider<InternalChangeQuery> queryProvider, 50 : RelatedChangesSorter sorter, 51 : IndexConfig indexConfig, 52 145 : ChangeData.Factory changeDataFactory) { 53 145 : this.queryProvider = queryProvider; 54 145 : this.sorter = sorter; 55 145 : this.indexConfig = indexConfig; 56 145 : this.changeDataFactory = changeDataFactory; 57 145 : } 58 : 59 : /** 60 : * Gets related changes of a specific change revision. 61 : * 62 : * @param changeData the change of the inputted revision. 63 : * @param basePs the revision that the method checks for related changes. 64 : * @return list of related changes, sorted via {@link RelatedChangesSorter} 65 : */ 66 : public List<RelatedChangesSorter.PatchSetData> getRelated(ChangeData changeData, PatchSet basePs) 67 : throws IOException, PermissionBackendException { 68 6 : Set<String> groups = getAllGroups(changeData.patchSets()); 69 6 : logger.atFine().log("groups = %s", groups); 70 6 : if (groups.isEmpty()) { 71 0 : return Collections.emptyList(); 72 : } 73 : 74 6 : List<ChangeData> cds = 75 6 : InternalChangeQuery.byProjectGroups( 76 6 : queryProvider, indexConfig, changeData.project(), groups); 77 6 : if (cds.isEmpty()) { 78 0 : return Collections.emptyList(); 79 : } 80 6 : if (cds.size() == 1 && cds.get(0).getId().equals(changeData.getId())) { 81 5 : return Collections.emptyList(); 82 : } 83 : 84 5 : cds = reloadChangeIfStale(cds, changeData, basePs); 85 : 86 5 : return sorter.sort(cds, basePs); 87 : } 88 : 89 : private List<ChangeData> reloadChangeIfStale( 90 : List<ChangeData> changeDatasFromIndex, ChangeData wantedChange, PatchSet wantedPs) { 91 5 : checkArgument( 92 5 : wantedChange.getId().equals(wantedPs.id().changeId()), 93 : "change of wantedPs (%s) doesn't match wantedChange (%s)", 94 5 : wantedPs.id().changeId(), 95 5 : wantedChange.getId()); 96 : 97 5 : List<ChangeData> changeDatas = new ArrayList<>(changeDatasFromIndex.size() + 1); 98 5 : changeDatas.addAll(changeDatasFromIndex); 99 : 100 : // Reload the change in case the patch set is absent. 101 5 : changeDatas.stream() 102 5 : .filter( 103 5 : cd -> cd.getId().equals(wantedPs.id().changeId()) && cd.patchSet(wantedPs.id()) == null) 104 5 : .forEach(ChangeData::reloadChange); 105 : 106 5 : if (changeDatas.stream().noneMatch(cd -> cd.getId().equals(wantedPs.id().changeId()))) { 107 : // The change of the wanted patch set is missing in the result from the index. 108 : // Load it from NoteDb and add it to the result. 109 0 : changeDatas.add(changeDataFactory.create(wantedChange.change())); 110 : } 111 : 112 5 : return changeDatas; 113 : } 114 : 115 : @VisibleForTesting 116 : public static Set<String> getAllGroups(Collection<PatchSet> patchSets) { 117 6 : return patchSets.stream().flatMap(ps -> ps.groups().stream()).collect(toSet()); 118 : } 119 : }