LCOV - code coverage report
Current view: top level - server/restapi/change - GetDiff.java (source / functions) Hit Total Coverage
Test: _coverage_report.dat Lines: 92 107 86.0 %
Date: 2022-11-19 15:00:39 Functions: 17 21 81.0 %

          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.restapi.change;
      16             : 
      17             : import static com.google.gerrit.server.project.ProjectCache.illegalState;
      18             : 
      19             : import com.google.common.base.MoreObjects;
      20             : import com.google.common.collect.ImmutableList;
      21             : import com.google.common.flogger.FluentLogger;
      22             : import com.google.gerrit.common.Nullable;
      23             : import com.google.gerrit.common.data.PatchScript;
      24             : import com.google.gerrit.entities.PatchSet;
      25             : import com.google.gerrit.entities.Project;
      26             : import com.google.gerrit.extensions.client.DiffPreferencesInfo;
      27             : import com.google.gerrit.extensions.client.DiffPreferencesInfo.Whitespace;
      28             : import com.google.gerrit.extensions.common.DiffInfo;
      29             : import com.google.gerrit.extensions.common.DiffWebLinkInfo;
      30             : import com.google.gerrit.extensions.common.WebLinkInfo;
      31             : import com.google.gerrit.extensions.restapi.AuthException;
      32             : import com.google.gerrit.extensions.restapi.BadRequestException;
      33             : import com.google.gerrit.extensions.restapi.CacheControl;
      34             : import com.google.gerrit.extensions.restapi.IdString;
      35             : import com.google.gerrit.extensions.restapi.ResourceConflictException;
      36             : import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
      37             : import com.google.gerrit.extensions.restapi.Response;
      38             : import com.google.gerrit.extensions.restapi.RestReadView;
      39             : import com.google.gerrit.server.CurrentUser;
      40             : import com.google.gerrit.server.WebLinks;
      41             : import com.google.gerrit.server.change.FileResource;
      42             : import com.google.gerrit.server.change.RevisionResource;
      43             : import com.google.gerrit.server.diff.DiffInfoCreator;
      44             : import com.google.gerrit.server.diff.DiffSide;
      45             : import com.google.gerrit.server.diff.DiffWebLinksProvider;
      46             : import com.google.gerrit.server.git.LargeObjectException;
      47             : import com.google.gerrit.server.notedb.ChangeNotes;
      48             : import com.google.gerrit.server.patch.PatchScriptFactory;
      49             : import com.google.gerrit.server.permissions.PermissionBackendException;
      50             : import com.google.gerrit.server.project.InvalidChangeOperationException;
      51             : import com.google.gerrit.server.project.NoSuchChangeException;
      52             : import com.google.gerrit.server.project.ProjectCache;
      53             : import com.google.gerrit.server.project.ProjectState;
      54             : import com.google.inject.Inject;
      55             : import com.google.inject.Provider;
      56             : import java.io.IOException;
      57             : import java.util.concurrent.TimeUnit;
      58             : import org.kohsuke.args4j.CmdLineParser;
      59             : import org.kohsuke.args4j.Option;
      60             : import org.kohsuke.args4j.OptionDef;
      61             : import org.kohsuke.args4j.spi.OptionHandler;
      62             : import org.kohsuke.args4j.spi.Parameters;
      63             : import org.kohsuke.args4j.spi.Setter;
      64             : 
      65             : public class GetDiff implements RestReadView<FileResource> {
      66          16 :   private static final FluentLogger logger = FluentLogger.forEnclosingClass();
      67             : 
      68             :   private final ProjectCache projectCache;
      69             :   private final PatchScriptFactory.Factory patchScriptFactoryFactory;
      70             :   private final Revisions revisions;
      71             :   private final WebLinks webLinks;
      72             :   private final Provider<CurrentUser> currentUser;
      73             : 
      74             :   @Option(name = "--base", metaVar = "REVISION")
      75             :   String base;
      76             : 
      77             :   /** 1-based index of the parent's position in the commit object. */
      78             :   @Option(name = "--parent", metaVar = "parent-number")
      79             :   int parentNum;
      80             : 
      81             :   @Deprecated
      82             :   @Option(name = "--ignore-whitespace")
      83             :   IgnoreWhitespace ignoreWhitespace;
      84             : 
      85             :   @Option(name = "--whitespace")
      86             :   Whitespace whitespace;
      87             : 
      88             :   // TODO(hiesel): Remove parameter when not used by callers (e.g. frontend) anymore.
      89             :   @Option(name = "--context", handler = ContextOptionHandler.class)
      90             :   int context;
      91             : 
      92             :   @Option(name = "--intraline")
      93             :   boolean intraline;
      94             : 
      95             :   @Inject
      96             :   GetDiff(
      97             :       ProjectCache projectCache,
      98             :       PatchScriptFactory.Factory patchScriptFactoryFactory,
      99             :       Revisions revisions,
     100             :       WebLinks webLinks,
     101          16 :       Provider<CurrentUser> currentUser) {
     102          16 :     this.projectCache = projectCache;
     103          16 :     this.patchScriptFactoryFactory = patchScriptFactoryFactory;
     104          16 :     this.revisions = revisions;
     105          16 :     this.webLinks = webLinks;
     106          16 :     this.currentUser = currentUser;
     107          16 :   }
     108             : 
     109             :   @Override
     110             :   public Response<DiffInfo> apply(FileResource resource)
     111             :       throws BadRequestException, ResourceConflictException, ResourceNotFoundException,
     112             :           AuthException, InvalidChangeOperationException, IOException, PermissionBackendException {
     113           8 :     DiffPreferencesInfo prefs = new DiffPreferencesInfo();
     114           8 :     if (whitespace != null) {
     115           2 :       prefs.ignoreWhitespace = whitespace;
     116           8 :     } else if (ignoreWhitespace != null) {
     117           0 :       prefs.ignoreWhitespace = ignoreWhitespace.whitespace;
     118             :     } else {
     119           8 :       prefs.ignoreWhitespace = Whitespace.IGNORE_LEADING_AND_TRAILING;
     120             :     }
     121           8 :     prefs.intralineDifference = intraline;
     122           8 :     logger.atFine().log(
     123             :         "diff preferences: ignoreWhitespace = %s, intralineDifference = %s",
     124             :         prefs.ignoreWhitespace, prefs.intralineDifference);
     125             : 
     126             :     PatchScriptFactory psf;
     127           8 :     PatchSet basePatchSet = null;
     128           8 :     PatchSet.Id pId = resource.getPatchKey().patchSetId();
     129           8 :     String fileName = resource.getPatchKey().fileName();
     130           8 :     logger.atFine().log(
     131             :         "patchSetId = %d, fileName = %s, base = %s, parentNum = %d",
     132           8 :         pId.get(), fileName, base, parentNum);
     133           8 :     ChangeNotes notes = resource.getRevision().getNotes();
     134           8 :     if (base != null) {
     135           3 :       RevisionResource baseResource =
     136           3 :           revisions.parse(resource.getRevision().getChangeResource(), IdString.fromDecoded(base));
     137           3 :       basePatchSet = baseResource.getPatchSet();
     138           3 :       if (basePatchSet.id().get() == 0) {
     139           2 :         throw new BadRequestException("edit not allowed as base");
     140             :       }
     141           3 :       psf =
     142           3 :           patchScriptFactoryFactory.create(
     143           3 :               notes, fileName, basePatchSet.id(), pId, prefs, currentUser.get());
     144           8 :     } else if (parentNum > 0) {
     145           3 :       psf =
     146           3 :           patchScriptFactoryFactory.create(
     147           3 :               notes, fileName, parentNum, pId, prefs, currentUser.get());
     148             :     } else {
     149           7 :       psf = patchScriptFactoryFactory.create(notes, fileName, null, pId, prefs, currentUser.get());
     150             :     }
     151             : 
     152             :     try {
     153           8 :       PatchScript ps = psf.call();
     154           8 :       Project.NameKey projectName = resource.getRevision().getChange().getProject();
     155           8 :       ProjectState state = projectCache.get(projectName).orElseThrow(illegalState(projectName));
     156           8 :       DiffSide sideA =
     157           8 :           DiffSide.create(
     158           8 :               ps.getFileInfoA(),
     159           8 :               MoreObjects.firstNonNull(ps.getOldName(), ps.getNewName()),
     160             :               DiffSide.Type.SIDE_A);
     161           8 :       DiffSide sideB = DiffSide.create(ps.getFileInfoB(), ps.getNewName(), DiffSide.Type.SIDE_B);
     162           8 :       DiffWebLinksProvider webLinksProvider =
     163             :           new DiffWebLinksProviderImpl(sideA, sideB, projectName, basePatchSet, webLinks, resource);
     164           8 :       DiffInfoCreator diffInfoCreator = new DiffInfoCreator(state, webLinksProvider, intraline);
     165           8 :       DiffInfo result = diffInfoCreator.create(ps, sideA, sideB);
     166             : 
     167           8 :       Response<DiffInfo> r = Response.ok(result);
     168           8 :       if (resource.isCacheable()) {
     169           4 :         r.caching(CacheControl.PRIVATE(7, TimeUnit.DAYS));
     170             :       }
     171           8 :       return r;
     172           0 :     } catch (NoSuchChangeException e) {
     173           0 :       throw new ResourceNotFoundException(e.getMessage(), e);
     174           0 :     } catch (LargeObjectException e) {
     175           0 :       throw new ResourceConflictException(e.getMessage(), e);
     176             :     }
     177             :   }
     178             : 
     179             :   private static class DiffWebLinksProviderImpl implements DiffWebLinksProvider {
     180             : 
     181             :     private final WebLinks webLinks;
     182             :     private final Project.NameKey projectName;
     183             :     private final DiffSide sideA;
     184             :     private final DiffSide sideB;
     185             :     private final String revA;
     186             :     private final String revB;
     187             :     private final String hashA;
     188             :     private final String hashB;
     189             :     private final FileResource resource;
     190             :     @Nullable private final PatchSet basePatchSet;
     191             : 
     192             :     DiffWebLinksProviderImpl(
     193             :         DiffSide sideA,
     194             :         DiffSide sideB,
     195             :         Project.NameKey projectName,
     196             :         @Nullable PatchSet basePatchSet,
     197             :         WebLinks webLinks,
     198           8 :         FileResource resource) {
     199           8 :       this.projectName = projectName;
     200           8 :       this.webLinks = webLinks;
     201           8 :       this.basePatchSet = basePatchSet;
     202           8 :       this.resource = resource;
     203           8 :       this.sideA = sideA;
     204           8 :       this.sideB = sideB;
     205             : 
     206           8 :       revA = basePatchSet != null ? basePatchSet.refName() : sideA.fileInfo().commitId;
     207           8 :       hashA = sideA.fileInfo().commitId;
     208             : 
     209           8 :       RevisionResource revision = resource.getRevision();
     210           8 :       revB =
     211             :           revision
     212           8 :               .getEdit()
     213           8 :               .map(edit -> edit.getRefName())
     214           8 :               .orElseGet(() -> revision.getPatchSet().refName());
     215           8 :       hashB = sideB.fileInfo().commitId;
     216             : 
     217           8 :       logger.atFine().log("revA = %s, hashA = %s, revB = %s, hashB = %s", revA, hashA, revB, hashB);
     218           8 :     }
     219             : 
     220             :     @Override
     221             :     public ImmutableList<DiffWebLinkInfo> getDiffLinks() {
     222           8 :       return webLinks.getDiffLinks(
     223           8 :           projectName.get(),
     224           8 :           resource.getPatchKey().patchSetId().changeId().get(),
     225           8 :           basePatchSet != null ? basePatchSet.id().get() : null,
     226             :           revA,
     227           8 :           sideA.fileName(),
     228           8 :           resource.getPatchKey().patchSetId().get(),
     229             :           revB,
     230           8 :           sideB.fileName());
     231             :     }
     232             : 
     233             :     @Override
     234             :     public ImmutableList<WebLinkInfo> getEditWebLinks() {
     235           8 :       return webLinks.getEditLinks(projectName.get(), revB, sideB.fileName());
     236             :     }
     237             : 
     238             :     @Override
     239             :     public ImmutableList<WebLinkInfo> getFileWebLinks(DiffSide.Type type) {
     240           8 :       String rev = getSideRev(type);
     241           8 :       String hash = getSideHash(type);
     242           8 :       DiffSide side = getDiffSide(type);
     243           8 :       return webLinks.getFileLinks(projectName.get(), rev, hash, side.fileName());
     244             :     }
     245             : 
     246             :     private String getSideRev(DiffSide.Type sideType) {
     247           8 :       return DiffSide.Type.SIDE_A == sideType ? revA : revB;
     248             :     }
     249             : 
     250             :     private String getSideHash(DiffSide.Type sideType) {
     251           8 :       return DiffSide.Type.SIDE_A == sideType ? hashA : hashB;
     252             :     }
     253             : 
     254             :     private DiffSide getDiffSide(DiffSide.Type sideType) {
     255           8 :       return DiffSide.Type.SIDE_A == sideType ? sideA : sideB;
     256             :     }
     257             :   }
     258             : 
     259             :   public GetDiff setBase(String base) {
     260           3 :     this.base = base;
     261           3 :     return this;
     262             :   }
     263             : 
     264             :   public GetDiff setParent(int parentNum) {
     265           3 :     this.parentNum = parentNum;
     266           3 :     return this;
     267             :   }
     268             : 
     269             :   public GetDiff setIntraline(boolean intraline) {
     270           2 :     this.intraline = intraline;
     271           2 :     return this;
     272             :   }
     273             : 
     274             :   public GetDiff setWhitespace(Whitespace whitespace) {
     275           2 :     this.whitespace = whitespace;
     276           2 :     return this;
     277             :   }
     278             : 
     279           0 :   @Deprecated
     280             :   enum IgnoreWhitespace {
     281           0 :     NONE(DiffPreferencesInfo.Whitespace.IGNORE_NONE),
     282           0 :     TRAILING(DiffPreferencesInfo.Whitespace.IGNORE_TRAILING),
     283           0 :     CHANGED(DiffPreferencesInfo.Whitespace.IGNORE_LEADING_AND_TRAILING),
     284           0 :     ALL(DiffPreferencesInfo.Whitespace.IGNORE_ALL);
     285             : 
     286             :     private final DiffPreferencesInfo.Whitespace whitespace;
     287             : 
     288           0 :     IgnoreWhitespace(DiffPreferencesInfo.Whitespace whitespace) {
     289           0 :       this.whitespace = whitespace;
     290           0 :     }
     291             :   }
     292             : 
     293             :   // TODO(hiesel): Remove this class once clients don't send the context parameter anymore.
     294             :   public static class ContextOptionHandler extends OptionHandler<Short> {
     295             : 
     296             :     public ContextOptionHandler(CmdLineParser parser, OptionDef option, Setter<Short> setter) {
     297           2 :       super(parser, option, setter);
     298           2 :     }
     299             : 
     300             :     @Override
     301             :     public final int parseArguments(Parameters params) {
     302             :       // Return 1 to consume the context parameter.
     303           0 :       return 1;
     304             :     }
     305             : 
     306             :     @Override
     307             :     public final String getDefaultMetaVariable() {
     308           0 :       return "ignored";
     309             :     }
     310             :   }
     311             : }

Generated by: LCOV version 1.16+git.20220603.dfeb750