Add parent parameter to GetContent
PolyGerrit has a hard time showing the diff view for binary files like images. To get the base, it has to request it separately using the parent commit SHA1, which is expensive. This commit adds a 'parent' parameter to GetContent in the same way that it is done for DownloadContent. This way, PolyGerrit can use the same endpoint and avoid expensive permission checks since users who can see a change are also allowed to see the base commit. Change-Id: Id6b4d0ea994282d9b4942f9ec1a8f4071a1064b1
This commit is contained in:
@@ -4636,6 +4636,11 @@ is the 1-based index of the parent's position in the commit object.
|
|||||||
|
|
||||||
Gets the content of a file from a certain revision.
|
Gets the content of a file from a certain revision.
|
||||||
|
|
||||||
|
The optional, integer-valued `parent` parameter can be specified to request
|
||||||
|
the named file from a parent commit of the specified revision. The value is
|
||||||
|
the 1-based index of the parent's position in the commit object. If the
|
||||||
|
parameter is omitted or the value is non-positive, the patch set is referenced.
|
||||||
|
|
||||||
.Request
|
.Request
|
||||||
----
|
----
|
||||||
GET /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/revisions/674ac754f91e64a0efb8087e59a176484bd534d1/files/gerrit-server%2Fsrc%2Fmain%2Fjava%2Fcom%2Fgoogle%2Fgerrit%2Fserver%2Fproject%2FRefControl.java/content HTTP/1.0
|
GET /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/revisions/674ac754f91e64a0efb8087e59a176484bd534d1/files/gerrit-server%2Fsrc%2Fmain%2Fjava%2Fcom%2Fgoogle%2Fgerrit%2Fserver%2Fproject%2FRefControl.java/content HTTP/1.0
|
||||||
|
@@ -21,6 +21,7 @@ import static com.google.gerrit.extensions.api.changes.SubmittedTogetherOption.N
|
|||||||
import static com.google.gerrit.reviewdb.client.Patch.COMMIT_MSG;
|
import static com.google.gerrit.reviewdb.client.Patch.COMMIT_MSG;
|
||||||
import static com.google.gerrit.reviewdb.client.Patch.MERGE_LIST;
|
import static com.google.gerrit.reviewdb.client.Patch.MERGE_LIST;
|
||||||
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
|
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
|
||||||
|
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||||
import static java.util.stream.Collectors.toList;
|
import static java.util.stream.Collectors.toList;
|
||||||
import static org.eclipse.jgit.lib.Constants.HEAD;
|
import static org.eclipse.jgit.lib.Constants.HEAD;
|
||||||
import static org.eclipse.jgit.lib.Constants.R_TAGS;
|
import static org.eclipse.jgit.lib.Constants.R_TAGS;
|
||||||
@@ -110,6 +111,7 @@ import com.google.gwtorm.server.OrmException;
|
|||||||
import com.google.gwtorm.server.SchemaFactory;
|
import com.google.gwtorm.server.SchemaFactory;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.Provider;
|
import com.google.inject.Provider;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
@@ -1237,4 +1239,18 @@ public abstract class AbstractDaemonTest {
|
|||||||
projectsToWatch.add(pwi);
|
projectsToWatch.add(pwi);
|
||||||
gApi.accounts().self().setWatchedProjects(projectsToWatch);
|
gApi.accounts().self().setWatchedProjects(projectsToWatch);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void assertContent(PushOneCommit.Result pushResult, String path, String expectedContent)
|
||||||
|
throws Exception {
|
||||||
|
BinaryResult bin =
|
||||||
|
gApi.changes()
|
||||||
|
.id(pushResult.getChangeId())
|
||||||
|
.revision(pushResult.getCommit().name())
|
||||||
|
.file(path)
|
||||||
|
.content();
|
||||||
|
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||||
|
bin.writeTo(os);
|
||||||
|
String res = new String(os.toByteArray(), UTF_8);
|
||||||
|
assertThat(res).isEqualTo(expectedContent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1062,20 +1062,6 @@ public class RevisionIT extends AbstractDaemonTest {
|
|||||||
return eTag;
|
return eTag;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertContent(PushOneCommit.Result pushResult, String path, String expectedContent)
|
|
||||||
throws Exception {
|
|
||||||
BinaryResult bin =
|
|
||||||
gApi.changes()
|
|
||||||
.id(pushResult.getChangeId())
|
|
||||||
.revision(pushResult.getCommit().name())
|
|
||||||
.file(path)
|
|
||||||
.content();
|
|
||||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
|
||||||
bin.writeTo(os);
|
|
||||||
String res = new String(os.toByteArray(), UTF_8);
|
|
||||||
assertThat(res).isEqualTo(expectedContent);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void assertDiffForNewFile(
|
private void assertDiffForNewFile(
|
||||||
PushOneCommit.Result pushResult, String path, String expectedContentSideB) throws Exception {
|
PushOneCommit.Result pushResult, String path, String expectedContentSideB) throws Exception {
|
||||||
DiffInfo diff =
|
DiffInfo diff =
|
||||||
|
@@ -0,0 +1,7 @@
|
|||||||
|
load("//gerrit-acceptance-tests:tests.bzl", "acceptance_tests")
|
||||||
|
|
||||||
|
acceptance_tests(
|
||||||
|
srcs = glob(["*IT.java"]),
|
||||||
|
group = "rest_revision",
|
||||||
|
labels = ["rest"],
|
||||||
|
)
|
@@ -0,0 +1,78 @@
|
|||||||
|
// Copyright (C) 2017 The Android Open Source Project
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package com.google.gerrit.acceptance.rest.revision;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
import static com.google.gerrit.acceptance.PushOneCommit.FILE_CONTENT;
|
||||||
|
import static com.google.gerrit.acceptance.PushOneCommit.FILE_NAME;
|
||||||
|
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||||
|
|
||||||
|
import com.google.gerrit.acceptance.AbstractDaemonTest;
|
||||||
|
import com.google.gerrit.acceptance.PushOneCommit;
|
||||||
|
import com.google.gerrit.acceptance.RestResponse;
|
||||||
|
import com.google.gerrit.extensions.api.changes.ReviewInput;
|
||||||
|
import org.eclipse.jgit.util.Base64;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class RevisionIT extends AbstractDaemonTest {
|
||||||
|
@Test
|
||||||
|
public void contentOfParent() throws Exception {
|
||||||
|
String parentContent = "parent content";
|
||||||
|
PushOneCommit.Result parent = createChange("Parent change", FILE_NAME, parentContent);
|
||||||
|
parent.assertOkStatus();
|
||||||
|
|
||||||
|
gApi.changes().id(parent.getChangeId()).current().review(ReviewInput.approve());
|
||||||
|
gApi.changes().id(parent.getChangeId()).current().submit();
|
||||||
|
|
||||||
|
PushOneCommit.Result child = createChange("Child change", FILE_NAME, FILE_CONTENT);
|
||||||
|
child.assertOkStatus();
|
||||||
|
assertContent(child, FILE_NAME, FILE_CONTENT);
|
||||||
|
|
||||||
|
RestResponse response =
|
||||||
|
adminRestSession.get(
|
||||||
|
"/changes/"
|
||||||
|
+ child.getChangeId()
|
||||||
|
+ "/revisions/current/files/"
|
||||||
|
+ FILE_NAME
|
||||||
|
+ "/content?parent=1");
|
||||||
|
response.assertOK();
|
||||||
|
assertThat(new String(Base64.decode(response.getEntityContent()), UTF_8))
|
||||||
|
.isEqualTo(parentContent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void contentOfInvalidParent() throws Exception {
|
||||||
|
String parentContent = "parent content";
|
||||||
|
PushOneCommit.Result parent = createChange("Parent change", FILE_NAME, parentContent);
|
||||||
|
parent.assertOkStatus();
|
||||||
|
|
||||||
|
gApi.changes().id(parent.getChangeId()).current().review(ReviewInput.approve());
|
||||||
|
gApi.changes().id(parent.getChangeId()).current().submit();
|
||||||
|
|
||||||
|
PushOneCommit.Result child = createChange("Child change", FILE_NAME, FILE_CONTENT);
|
||||||
|
child.assertOkStatus();
|
||||||
|
assertContent(child, FILE_NAME, FILE_CONTENT);
|
||||||
|
|
||||||
|
RestResponse response =
|
||||||
|
adminRestSession.get(
|
||||||
|
"/changes/"
|
||||||
|
+ child.getChangeId()
|
||||||
|
+ "/revisions/current/files/"
|
||||||
|
+ FILE_NAME
|
||||||
|
+ "/content?parent=10");
|
||||||
|
response.assertBadRequest();
|
||||||
|
assertThat(response.getEntityContent()).isEqualTo("invalid parent");
|
||||||
|
}
|
||||||
|
}
|
@@ -397,8 +397,9 @@ public class ChangeEdits
|
|||||||
base
|
base
|
||||||
? ObjectId.fromString(edit.getBasePatchSet().getRevision().get())
|
? ObjectId.fromString(edit.getBasePatchSet().getRevision().get())
|
||||||
: edit.getEditCommit(),
|
: edit.getEditCommit(),
|
||||||
rsrc.getPath()));
|
rsrc.getPath(),
|
||||||
} catch (ResourceNotFoundException rnfe) {
|
null));
|
||||||
|
} catch (ResourceNotFoundException | BadRequestException e) {
|
||||||
return Response.none();
|
return Response.none();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -22,6 +22,7 @@ import com.google.common.hash.Hashing;
|
|||||||
import com.google.gerrit.common.Nullable;
|
import com.google.gerrit.common.Nullable;
|
||||||
import com.google.gerrit.common.TimeUtil;
|
import com.google.gerrit.common.TimeUtil;
|
||||||
import com.google.gerrit.common.data.PatchScript.FileMode;
|
import com.google.gerrit.common.data.PatchScript.FileMode;
|
||||||
|
import com.google.gerrit.extensions.restapi.BadRequestException;
|
||||||
import com.google.gerrit.extensions.restapi.BinaryResult;
|
import com.google.gerrit.extensions.restapi.BinaryResult;
|
||||||
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
|
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
|
||||||
import com.google.gerrit.reviewdb.client.Patch;
|
import com.google.gerrit.reviewdb.client.Patch;
|
||||||
@@ -67,9 +68,33 @@ public class FileContentUtil {
|
|||||||
this.registry = ftr;
|
this.registry = ftr;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BinaryResult getContent(ProjectState project, ObjectId revstr, String path)
|
/**
|
||||||
throws ResourceNotFoundException, IOException {
|
* Get the content of a file at a specific commit or one of it's parent commits.
|
||||||
try (Repository repo = openRepository(project)) {
|
*
|
||||||
|
* @param project A {@code Project} that this request refers to.
|
||||||
|
* @param revstr An {@code ObjectId} specifying the commit.
|
||||||
|
* @param path A string specifying the filepath.
|
||||||
|
* @param parent A 1-based parent index to get the content from instead. Null if the content
|
||||||
|
* should be obtained from {@param revstr} instead.
|
||||||
|
* @return Content of the file as {@code BinaryResult}.
|
||||||
|
* @throws ResourceNotFoundException
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public BinaryResult getContent(
|
||||||
|
ProjectState project, ObjectId revstr, String path, @Nullable Integer parent)
|
||||||
|
throws BadRequestException, ResourceNotFoundException, IOException {
|
||||||
|
try (Repository repo = openRepository(project);
|
||||||
|
RevWalk rw = new RevWalk(repo)) {
|
||||||
|
if (parent != null) {
|
||||||
|
RevCommit revCommit = rw.parseCommit(revstr);
|
||||||
|
if (revCommit == null) {
|
||||||
|
throw new ResourceNotFoundException("commit not found");
|
||||||
|
}
|
||||||
|
if (parent > revCommit.getParentCount()) {
|
||||||
|
throw new BadRequestException("invalid parent");
|
||||||
|
}
|
||||||
|
revstr = rw.parseCommit(revstr).getParent(Integer.max(0, parent - 1)).toObjectId();
|
||||||
|
}
|
||||||
return getContent(repo, project, revstr, path);
|
return getContent(repo, project, revstr, path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
package com.google.gerrit.server.change;
|
package com.google.gerrit.server.change;
|
||||||
|
|
||||||
|
import com.google.gerrit.extensions.restapi.BadRequestException;
|
||||||
import com.google.gerrit.extensions.restapi.BinaryResult;
|
import com.google.gerrit.extensions.restapi.BinaryResult;
|
||||||
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
|
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
|
||||||
import com.google.gerrit.extensions.restapi.RestReadView;
|
import com.google.gerrit.extensions.restapi.RestReadView;
|
||||||
@@ -30,21 +31,23 @@ import com.google.gerrit.server.project.NoSuchChangeException;
|
|||||||
import com.google.gwtorm.server.OrmException;
|
import com.google.gwtorm.server.OrmException;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.Provider;
|
import com.google.inject.Provider;
|
||||||
import com.google.inject.Singleton;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import org.eclipse.jgit.errors.RepositoryNotFoundException;
|
import org.eclipse.jgit.errors.RepositoryNotFoundException;
|
||||||
import org.eclipse.jgit.lib.ObjectId;
|
import org.eclipse.jgit.lib.ObjectId;
|
||||||
import org.eclipse.jgit.lib.Repository;
|
import org.eclipse.jgit.lib.Repository;
|
||||||
import org.eclipse.jgit.revwalk.RevCommit;
|
import org.eclipse.jgit.revwalk.RevCommit;
|
||||||
import org.eclipse.jgit.revwalk.RevWalk;
|
import org.eclipse.jgit.revwalk.RevWalk;
|
||||||
|
import org.kohsuke.args4j.Option;
|
||||||
|
|
||||||
@Singleton
|
|
||||||
public class GetContent implements RestReadView<FileResource> {
|
public class GetContent implements RestReadView<FileResource> {
|
||||||
private final Provider<ReviewDb> db;
|
private final Provider<ReviewDb> db;
|
||||||
private final GitRepositoryManager gitManager;
|
private final GitRepositoryManager gitManager;
|
||||||
private final PatchSetUtil psUtil;
|
private final PatchSetUtil psUtil;
|
||||||
private final FileContentUtil fileContentUtil;
|
private final FileContentUtil fileContentUtil;
|
||||||
|
|
||||||
|
@Option(name = "--parent")
|
||||||
|
private Integer parent;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
GetContent(
|
GetContent(
|
||||||
Provider<ReviewDb> db,
|
Provider<ReviewDb> db,
|
||||||
@@ -59,7 +62,7 @@ public class GetContent implements RestReadView<FileResource> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BinaryResult apply(FileResource rsrc)
|
public BinaryResult apply(FileResource rsrc)
|
||||||
throws ResourceNotFoundException, IOException, NoSuchChangeException, OrmException {
|
throws ResourceNotFoundException, IOException, BadRequestException, OrmException {
|
||||||
String path = rsrc.getPatchKey().get();
|
String path = rsrc.getPatchKey().get();
|
||||||
if (Patch.COMMIT_MSG.equals(path)) {
|
if (Patch.COMMIT_MSG.equals(path)) {
|
||||||
String msg = getMessage(rsrc.getRevision().getChangeResource().getNotes());
|
String msg = getMessage(rsrc.getRevision().getChangeResource().getNotes());
|
||||||
@@ -75,7 +78,8 @@ public class GetContent implements RestReadView<FileResource> {
|
|||||||
return fileContentUtil.getContent(
|
return fileContentUtil.getContent(
|
||||||
rsrc.getRevision().getControl().getProjectControl().getProjectState(),
|
rsrc.getRevision().getControl().getProjectControl().getProjectState(),
|
||||||
ObjectId.fromString(rsrc.getRevision().getPatchSet().getRevision().get()),
|
ObjectId.fromString(rsrc.getRevision().getPatchSet().getRevision().get()),
|
||||||
path);
|
path,
|
||||||
|
parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getMessage(ChangeNotes notes) throws OrmException, IOException {
|
private String getMessage(ChangeNotes notes) throws OrmException, IOException {
|
||||||
|
@@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
package com.google.gerrit.server.project;
|
package com.google.gerrit.server.project;
|
||||||
|
|
||||||
|
import com.google.gerrit.extensions.restapi.BadRequestException;
|
||||||
import com.google.gerrit.extensions.restapi.BinaryResult;
|
import com.google.gerrit.extensions.restapi.BinaryResult;
|
||||||
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
|
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
|
||||||
import com.google.gerrit.extensions.restapi.RestReadView;
|
import com.google.gerrit.extensions.restapi.RestReadView;
|
||||||
@@ -32,8 +33,9 @@ public class GetContent implements RestReadView<FileResource> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BinaryResult apply(FileResource rsrc) throws ResourceNotFoundException, IOException {
|
public BinaryResult apply(FileResource rsrc)
|
||||||
|
throws ResourceNotFoundException, BadRequestException, IOException {
|
||||||
return fileContentUtil.getContent(
|
return fileContentUtil.getContent(
|
||||||
rsrc.getProject().getProjectState(), rsrc.getRev(), rsrc.getPath());
|
rsrc.getProject().getProjectState(), rsrc.getRev(), rsrc.getPath(), null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user