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.restapi.change; 16 : 17 : import static com.google.gerrit.entities.RefNames.changeMetaRef; 18 : 19 : import com.google.gerrit.common.Nullable; 20 : import com.google.gerrit.extensions.client.ListChangesOption; 21 : import com.google.gerrit.extensions.client.ListOption; 22 : import com.google.gerrit.extensions.common.ChangeInfo; 23 : import com.google.gerrit.extensions.common.ChangeInfoDiffer; 24 : import com.google.gerrit.extensions.common.ChangeInfoDifference; 25 : import com.google.gerrit.extensions.restapi.BadRequestException; 26 : import com.google.gerrit.extensions.restapi.PreconditionFailedException; 27 : import com.google.gerrit.extensions.restapi.Response; 28 : import com.google.gerrit.extensions.restapi.RestApiException; 29 : import com.google.gerrit.extensions.restapi.RestReadView; 30 : import com.google.gerrit.server.DynamicOptions; 31 : import com.google.gerrit.server.DynamicOptions.DynamicBean; 32 : import com.google.gerrit.server.change.ChangeResource; 33 : import com.google.gerrit.server.git.GitRepositoryManager; 34 : import com.google.inject.Inject; 35 : import com.google.inject.Provider; 36 : import java.io.IOException; 37 : import java.util.EnumSet; 38 : import java.util.HashMap; 39 : import java.util.Map; 40 : import org.eclipse.jgit.errors.InvalidObjectIdException; 41 : import org.eclipse.jgit.errors.MissingObjectException; 42 : import org.eclipse.jgit.lib.ObjectId; 43 : import org.eclipse.jgit.lib.Repository; 44 : import org.eclipse.jgit.revwalk.RevCommit; 45 : import org.eclipse.jgit.revwalk.RevWalk; 46 : import org.kohsuke.args4j.Option; 47 : 48 : /** Gets the diff for a change at two NoteDb meta SHA-1s. */ 49 : public class GetMetaDiff 50 : implements RestReadView<ChangeResource>, 51 : DynamicOptions.BeanReceiver, 52 : DynamicOptions.BeanProvider { 53 : 54 57 : private final EnumSet<ListChangesOption> options = EnumSet.noneOf(ListChangesOption.class); 55 57 : private final Map<String, DynamicBean> dynamicBeans = new HashMap<>(); 56 : 57 : private final Provider<GetChange> getChangeProvider; 58 : private final GitRepositoryManager repoManager; 59 : 60 : @Option(name = "-o", usage = "Output options") 61 : public void addOption(ListChangesOption o) { 62 1 : options.add(o); 63 1 : } 64 : 65 : @Option(name = "-O", usage = "Output option flags, in hex") 66 : void setOptionFlagsHex(String hex) throws BadRequestException { 67 0 : options.addAll(ListOption.fromHexString(ListChangesOption.class, hex)); 68 0 : } 69 : 70 57 : @Option(name = "--old", usage = "old NoteDb meta SHA-1") 71 : String oldMetaRevId = ""; 72 : 73 : public void setOldMetaRevId(@Nullable String oldMetaRevId) { 74 1 : this.oldMetaRevId = oldMetaRevId == null ? "" : oldMetaRevId; 75 1 : } 76 : 77 57 : @Option(name = "--meta", usage = "new NoteDb meta SHA-1") 78 : String metaRevId = ""; 79 : 80 : public void setNewMetaRevId(@Nullable String metaRevId) { 81 1 : this.metaRevId = metaRevId == null ? "" : metaRevId; 82 1 : } 83 : 84 : @Inject 85 57 : GetMetaDiff(Provider<GetChange> getChangeProvider, GitRepositoryManager repoManager) { 86 57 : this.getChangeProvider = getChangeProvider; 87 57 : this.repoManager = repoManager; 88 57 : } 89 : 90 : @Override 91 : public void setDynamicBean(String plugin, DynamicOptions.DynamicBean dynamicBean) { 92 0 : dynamicBeans.put(plugin, dynamicBean); 93 0 : } 94 : 95 : @Override 96 : public DynamicBean getDynamicBean(String plugin) { 97 0 : return dynamicBeans.get(plugin); 98 : } 99 : 100 : @Override 101 : public Response<ChangeInfoDifference> apply(ChangeResource resource) 102 : throws RestApiException, IOException { 103 1 : return Response.ok( 104 1 : ChangeInfoDiffer.getDifference(getOldChangeInfo(resource), getNewChangeInfo(resource))); 105 : } 106 : 107 : private ChangeInfo getOldChangeInfo(ChangeResource resource) 108 : throws RestApiException, IOException { 109 1 : GetChange getChange = createGetChange(); 110 1 : getChange.setMetaRevId(getOldMetaRevId(resource)); 111 : ChangeInfo oldChangeInfo; 112 : try { 113 1 : oldChangeInfo = getChange.apply(resource).value(); 114 1 : } catch (PreconditionFailedException e) { 115 1 : oldChangeInfo = new ChangeInfo(); 116 1 : } 117 1 : return oldChangeInfo; 118 : } 119 : 120 : private String getOldMetaRevId(ChangeResource resource) 121 : throws IOException, BadRequestException, PreconditionFailedException { 122 1 : if (!oldMetaRevId.isEmpty()) { 123 1 : return oldMetaRevId; 124 : } 125 1 : String newMetaRevId = getNewMetaRevId(resource); 126 1 : try (Repository repo = repoManager.openRepository(resource.getProject()); 127 1 : RevWalk rw = new RevWalk(repo)) { 128 1 : ObjectId resourceId = ObjectId.fromString(newMetaRevId); 129 1 : RevCommit commit = rw.parseCommit(resourceId); 130 1 : return commit.getParentCount() == 0 131 1 : ? resourceId.getName() 132 1 : : commit.getParent(0).getId().getName(); 133 1 : } catch (InvalidObjectIdException e) { 134 1 : throw new BadRequestException("invalid meta SHA1: " + newMetaRevId, e); 135 1 : } catch (MissingObjectException e) { 136 1 : throw new PreconditionFailedException(e.getMessage()); 137 : } 138 : } 139 : 140 : private ChangeInfo getNewChangeInfo(ChangeResource resource) 141 : throws RestApiException, IOException { 142 1 : GetChange getChange = createGetChange(); 143 1 : getChange.setMetaRevId(getNewMetaRevId(resource)); 144 1 : return getChange.apply(resource).value(); 145 : } 146 : 147 : private String getNewMetaRevId(ChangeResource resource) throws IOException { 148 1 : if (!metaRevId.isEmpty()) { 149 1 : return metaRevId; 150 : } 151 1 : try (Repository repo = repoManager.openRepository(resource.getProject())) { 152 1 : return repo.exactRef(changeMetaRef(resource.getId())).getObjectId().getName(); 153 : } 154 : } 155 : 156 : private GetChange createGetChange() { 157 1 : GetChange getChange = getChangeProvider.get(); 158 1 : options.forEach(getChange::addOption); 159 1 : dynamicBeans.forEach(getChange::setDynamicBean); 160 1 : return getChange; 161 : } 162 : }